~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

(gz) Remove bzrlib/util/elementtree/ package (Martin Packman)

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
from __future__ import absolute_import
 
18
 
 
19
import bzrlib.bzrdir
17
20
 
18
21
from cStringIO import StringIO
19
 
import sys
20
22
 
21
23
from bzrlib.lazy_import import lazy_import
22
24
lazy_import(globals(), """
23
 
from itertools import chain
 
25
import itertools
24
26
from bzrlib import (
25
 
        bzrdir,
26
 
        cache_utf8,
27
 
        cleanup,
28
 
        config as _mod_config,
29
 
        debug,
30
 
        errors,
31
 
        fetch,
32
 
        graph as _mod_graph,
33
 
        lockdir,
34
 
        lockable_files,
35
 
        remote,
36
 
        repository,
37
 
        revision as _mod_revision,
38
 
        rio,
39
 
        transport,
40
 
        ui,
41
 
        urlutils,
42
 
        )
43
 
from bzrlib.config import BranchConfig, TransportConfig
44
 
from bzrlib.tag import (
45
 
    BasicTags,
46
 
    DisabledTags,
 
27
    bzrdir,
 
28
    controldir,
 
29
    cache_utf8,
 
30
    cleanup,
 
31
    config as _mod_config,
 
32
    debug,
 
33
    errors,
 
34
    fetch,
 
35
    graph as _mod_graph,
 
36
    lockdir,
 
37
    lockable_files,
 
38
    remote,
 
39
    repository,
 
40
    revision as _mod_revision,
 
41
    rio,
 
42
    tag as _mod_tag,
 
43
    transport,
 
44
    ui,
 
45
    urlutils,
 
46
    vf_search,
47
47
    )
 
48
from bzrlib.i18n import gettext, ngettext
48
49
""")
49
50
 
 
51
# Explicitly import bzrlib.bzrdir so that the BzrProber
 
52
# is guaranteed to be registered.
 
53
import bzrlib.bzrdir
 
54
 
50
55
from bzrlib import (
 
56
    bzrdir,
51
57
    controldir,
52
58
    )
53
59
from bzrlib.decorators import (
66
72
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
67
73
 
68
74
 
69
 
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
70
 
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
71
 
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
72
 
 
73
 
 
74
75
class Branch(controldir.ControlComponent):
75
76
    """Branch holding a history of revisions.
76
77
 
93
94
    def user_transport(self):
94
95
        return self.bzrdir.user_transport
95
96
 
96
 
    def __init__(self, *ignored, **ignored_too):
 
97
    def __init__(self, possible_transports=None):
97
98
        self.tags = self._format.make_tags(self)
98
99
        self._revision_history_cache = None
99
100
        self._revision_id_to_revno_cache = None
103
104
        self._last_revision_info_cache = None
104
105
        self._master_branch_cache = None
105
106
        self._merge_sorted_revisions_cache = None
106
 
        self._open_hook()
 
107
        self._open_hook(possible_transports)
107
108
        hooks = Branch.hooks['open']
108
109
        for hook in hooks:
109
110
            hook(self)
110
111
 
111
 
    def _open_hook(self):
 
112
    def _open_hook(self, possible_transports):
112
113
        """Called by init to allow simpler extension of the base class."""
113
114
 
114
 
    def _activate_fallback_location(self, url):
 
115
    def _activate_fallback_location(self, url, possible_transports):
115
116
        """Activate the branch/repository from url as a fallback repository."""
116
117
        for existing_fallback_repo in self.repository._fallback_repositories:
117
118
            if existing_fallback_repo.user_url == url:
118
119
                # This fallback is already configured.  This probably only
119
 
                # happens because BzrDir.sprout is a horrible mess.  To avoid
 
120
                # happens because ControlDir.sprout is a horrible mess.  To avoid
120
121
                # confusing _unstack we don't add this a second time.
121
122
                mutter('duplicate activation of fallback %r on %r', url, self)
122
123
                return
123
 
        repo = self._get_fallback_repository(url)
 
124
        repo = self._get_fallback_repository(url, possible_transports)
124
125
        if repo.has_same_location(self.repository):
125
126
            raise errors.UnstackableLocationError(self.user_url, url)
126
127
        self.repository.add_fallback_repository(repo)
180
181
        For instance, if the branch is at URL/.bzr/branch,
181
182
        Branch.open(URL) -> a Branch instance.
182
183
        """
183
 
        control = bzrdir.BzrDir.open(base, _unsupported,
184
 
                                     possible_transports=possible_transports)
185
 
        return control.open_branch(unsupported=_unsupported)
 
184
        control = controldir.ControlDir.open(base,
 
185
            possible_transports=possible_transports, _unsupported=_unsupported)
 
186
        return control.open_branch(unsupported=_unsupported,
 
187
            possible_transports=possible_transports)
186
188
 
187
189
    @staticmethod
188
 
    def open_from_transport(transport, name=None, _unsupported=False):
 
190
    def open_from_transport(transport, name=None, _unsupported=False,
 
191
            possible_transports=None):
189
192
        """Open the branch rooted at transport"""
190
 
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
191
 
        return control.open_branch(name=name, unsupported=_unsupported)
 
193
        control = controldir.ControlDir.open_from_transport(transport, _unsupported)
 
194
        return control.open_branch(name=name, unsupported=_unsupported,
 
195
            possible_transports=possible_transports)
192
196
 
193
197
    @staticmethod
194
198
    def open_containing(url, possible_transports=None):
202
206
        format, UnknownFormatError or UnsupportedFormatError are raised.
203
207
        If there is one, it is returned, along with the unused portion of url.
204
208
        """
205
 
        control, relpath = bzrdir.BzrDir.open_containing(url,
 
209
        control, relpath = controldir.ControlDir.open_containing(url,
206
210
                                                         possible_transports)
207
 
        return control.open_branch(), relpath
 
211
        branch = control.open_branch(possible_transports=possible_transports)
 
212
        return (branch, relpath)
208
213
 
209
214
    def _push_should_merge_tags(self):
210
215
        """Should _basic_push merge this branch's tags into the target?
222
227
 
223
228
        :return: A bzrlib.config.BranchConfig.
224
229
        """
225
 
        return BranchConfig(self)
 
230
        return _mod_config.BranchConfig(self)
 
231
 
 
232
    def get_config_stack(self):
 
233
        """Get a bzrlib.config.BranchStack for this Branch.
 
234
 
 
235
        This can then be used to get and set configuration options for the
 
236
        branch.
 
237
 
 
238
        :return: A bzrlib.config.BranchStack.
 
239
        """
 
240
        return _mod_config.BranchStack(self)
226
241
 
227
242
    def _get_config(self):
228
243
        """Get the concrete config for just the config in this branch.
236
251
        """
237
252
        raise NotImplementedError(self._get_config)
238
253
 
239
 
    def _get_fallback_repository(self, url):
 
254
    def _get_fallback_repository(self, url, possible_transports):
240
255
        """Get the repository we fallback to at url."""
241
256
        url = urlutils.join(self.base, url)
242
 
        a_branch = Branch.open(url,
243
 
            possible_transports=[self.bzrdir.root_transport])
 
257
        a_branch = Branch.open(url, possible_transports=possible_transports)
244
258
        return a_branch.repository
245
259
 
246
260
    @needs_read_lock
513
527
        rev_iter = iter(merge_sorted_revisions)
514
528
        if start_revision_id is not None:
515
529
            for node in rev_iter:
516
 
                rev_id = node.key[-1]
 
530
                rev_id = node.key
517
531
                if rev_id != start_revision_id:
518
532
                    continue
519
533
                else:
520
534
                    # The decision to include the start or not
521
535
                    # depends on the stop_rule if a stop is provided
522
536
                    # so pop this node back into the iterator
523
 
                    rev_iter = chain(iter([node]), rev_iter)
 
537
                    rev_iter = itertools.chain(iter([node]), rev_iter)
524
538
                    break
525
539
        if stop_revision_id is None:
526
540
            # Yield everything
527
541
            for node in rev_iter:
528
 
                rev_id = node.key[-1]
 
542
                rev_id = node.key
529
543
                yield (rev_id, node.merge_depth, node.revno,
530
544
                       node.end_of_merge)
531
545
        elif stop_rule == 'exclude':
532
546
            for node in rev_iter:
533
 
                rev_id = node.key[-1]
 
547
                rev_id = node.key
534
548
                if rev_id == stop_revision_id:
535
549
                    return
536
550
                yield (rev_id, node.merge_depth, node.revno,
537
551
                       node.end_of_merge)
538
552
        elif stop_rule == 'include':
539
553
            for node in rev_iter:
540
 
                rev_id = node.key[-1]
 
554
                rev_id = node.key
541
555
                yield (rev_id, node.merge_depth, node.revno,
542
556
                       node.end_of_merge)
543
557
                if rev_id == stop_revision_id:
549
563
            ancestors = graph.find_unique_ancestors(start_revision_id,
550
564
                                                    [stop_revision_id])
551
565
            for node in rev_iter:
552
 
                rev_id = node.key[-1]
 
566
                rev_id = node.key
553
567
                if rev_id not in ancestors:
554
568
                    continue
555
569
                yield (rev_id, node.merge_depth, node.revno,
565
579
            reached_stop_revision_id = False
566
580
            revision_id_whitelist = []
567
581
            for node in rev_iter:
568
 
                rev_id = node.key[-1]
 
582
                rev_id = node.key
569
583
                if rev_id == left_parent:
570
584
                    # reached the left parent after the stop_revision
571
585
                    return
651
665
        """
652
666
        raise errors.UpgradeRequired(self.user_url)
653
667
 
 
668
    def get_append_revisions_only(self):
 
669
        """Whether it is only possible to append revisions to the history.
 
670
        """
 
671
        if not self._format.supports_set_append_revisions_only():
 
672
            return False
 
673
        return self.get_config_stack().get('append_revisions_only')
 
674
 
654
675
    def set_append_revisions_only(self, enabled):
655
676
        if not self._format.supports_set_append_revisions_only():
656
677
            raise errors.UpgradeRequired(self.user_url)
657
 
        if enabled:
658
 
            value = 'True'
659
 
        else:
660
 
            value = 'False'
661
 
        self.get_config().set_user_option('append_revisions_only', value,
662
 
            warn_masked=True)
 
678
        self.get_config_stack().set('append_revisions_only', enabled)
663
679
 
664
680
    def set_reference_info(self, file_id, tree_path, branch_location):
665
681
        """Set the branch location to use for a tree reference."""
694
710
        """
695
711
        raise errors.UpgradeRequired(self.user_url)
696
712
 
697
 
    def get_commit_builder(self, parents, config=None, timestamp=None,
 
713
    def get_commit_builder(self, parents, config_stack=None, timestamp=None,
698
714
                           timezone=None, committer=None, revprops=None,
699
715
                           revision_id=None, lossy=False):
700
716
        """Obtain a CommitBuilder for this branch.
710
726
            represented, when pushing to a foreign VCS 
711
727
        """
712
728
 
713
 
        if config is None:
714
 
            config = self.get_config()
 
729
        if config_stack is None:
 
730
            config_stack = self.get_config_stack()
715
731
 
716
 
        return self.repository.get_commit_builder(self, parents, config,
 
732
        return self.repository.get_commit_builder(self, parents, config_stack,
717
733
            timestamp, timezone, committer, revprops, revision_id,
718
734
            lossy)
719
735
 
724
740
        """
725
741
        return None
726
742
 
 
743
    @deprecated_method(deprecated_in((2, 5, 0)))
727
744
    def get_revision_delta(self, revno):
728
745
        """Return the delta for one revision.
729
746
 
730
747
        The delta is relative to its mainline predecessor, or the
731
748
        empty tree for revision 1.
732
749
        """
733
 
        rh = self.revision_history()
734
 
        if not (1 <= revno <= len(rh)):
 
750
        try:
 
751
            revid = self.get_rev_id(revno)
 
752
        except errors.NoSuchRevision:
735
753
            raise errors.InvalidRevisionNumber(revno)
736
 
        return self.repository.get_revision_delta(rh[revno-1])
 
754
        return self.repository.get_revision_delta(revid)
737
755
 
738
756
    def get_stacked_on_url(self):
739
757
        """Get the URL this branch is stacked against.
785
803
                                  other_branch=None):
786
804
        """See Branch.generate_revision_history"""
787
805
        graph = self.repository.get_graph()
 
806
        (last_revno, last_revid) = self.last_revision_info()
788
807
        known_revision_ids = [
789
 
            self.last_revision_info(),
 
808
            (last_revid, last_revno),
790
809
            (_mod_revision.NULL_REVISION, 0),
791
810
            ]
792
811
        if last_rev is not None:
837
856
                return
838
857
            self._unstack()
839
858
        else:
840
 
            self._activate_fallback_location(url)
 
859
            self._activate_fallback_location(url,
 
860
                possible_transports=[self.bzrdir.root_transport])
841
861
        # write this out after the repository is stacked to avoid setting a
842
862
        # stacked config that doesn't work.
843
863
        self._set_config_location('stacked_on_location', url)
849
869
        """
850
870
        pb = ui.ui_factory.nested_progress_bar()
851
871
        try:
852
 
            pb.update("Unstacking")
 
872
            pb.update(gettext("Unstacking"))
853
873
            # The basic approach here is to fetch the tip of the branch,
854
874
            # including all available ghosts, from the existing stacked
855
875
            # repository into a new repository object without the fallbacks. 
869
889
            # stream from one of them to the other.  This does mean doing
870
890
            # separate SSH connection setup, but unstacking is not a
871
891
            # common operation so it's tolerable.
872
 
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
 
892
            new_bzrdir = controldir.ControlDir.open(
 
893
                self.bzrdir.root_transport.base)
873
894
            new_repository = new_bzrdir.find_repository()
874
895
            if new_repository._fallback_repositories:
875
896
                raise AssertionError("didn't expect %r to have "
918
939
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
919
940
                except errors.TagsNotSupported:
920
941
                    tags_to_fetch = set()
921
 
                fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
 
942
                fetch_spec = vf_search.NotInOtherForRevs(self.repository,
922
943
                    old_repository, required_ids=[self.last_revision()],
923
944
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
924
945
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
992
1013
        """
993
1014
        raise NotImplementedError(self._gen_revision_history)
994
1015
 
 
1016
    @deprecated_method(deprecated_in((2, 5, 0)))
995
1017
    @needs_read_lock
996
1018
    def revision_history(self):
997
1019
        """Return sequence of revision ids on this branch.
999
1021
        This method will cache the revision history for as long as it is safe to
1000
1022
        do so.
1001
1023
        """
 
1024
        return self._revision_history()
 
1025
 
 
1026
    def _revision_history(self):
1002
1027
        if 'evil' in debug.debug_flags:
1003
1028
            mutter_callsite(3, "revision_history scales with history.")
1004
1029
        if self._revision_history_cache is not None:
1074
1099
        """Given a revision id, return its revno"""
1075
1100
        if _mod_revision.is_null(revision_id):
1076
1101
            return 0
1077
 
        history = self.revision_history()
 
1102
        history = self._revision_history()
1078
1103
        try:
1079
1104
            return history.index(revision_id) + 1
1080
1105
        except ValueError:
1145
1170
    def _set_config_location(self, name, url, config=None,
1146
1171
                             make_relative=False):
1147
1172
        if config is None:
1148
 
            config = self.get_config()
 
1173
            config = self.get_config_stack()
1149
1174
        if url is None:
1150
1175
            url = ''
1151
1176
        elif make_relative:
1152
1177
            url = urlutils.relative_url(self.base, url)
1153
 
        config.set_user_option(name, url, warn_masked=True)
 
1178
        config.set(name, url)
1154
1179
 
1155
1180
    def _get_config_location(self, name, config=None):
1156
1181
        if config is None:
1157
 
            config = self.get_config()
1158
 
        location = config.get_user_option(name)
 
1182
            config = self.get_config_stack()
 
1183
        location = config.get(name)
1159
1184
        if location == '':
1160
1185
            location = None
1161
1186
        return location
1162
1187
 
1163
1188
    def get_child_submit_format(self):
1164
1189
        """Return the preferred format of submissions to this branch."""
1165
 
        return self.get_config().get_user_option("child_submit_format")
 
1190
        return self.get_config_stack().get('child_submit_format')
1166
1191
 
1167
1192
    def get_submit_branch(self):
1168
1193
        """Return the submit location of the branch.
1171
1196
        pattern is that the user can override it by specifying a
1172
1197
        location.
1173
1198
        """
1174
 
        return self.get_config().get_user_option('submit_branch')
 
1199
        return self.get_config_stack().get('submit_branch')
1175
1200
 
1176
1201
    def set_submit_branch(self, location):
1177
1202
        """Return the submit location of the branch.
1180
1205
        pattern is that the user can override it by specifying a
1181
1206
        location.
1182
1207
        """
1183
 
        self.get_config().set_user_option('submit_branch', location,
1184
 
            warn_masked=True)
 
1208
        self.get_config_stack().set('submit_branch', location)
1185
1209
 
1186
1210
    def get_public_branch(self):
1187
1211
        """Return the public location of the branch.
1200
1224
        self._set_config_location('public_branch', location)
1201
1225
 
1202
1226
    def get_push_location(self):
1203
 
        """Return the None or the location to push this branch to."""
1204
 
        push_loc = self.get_config().get_user_option('push_location')
1205
 
        return push_loc
 
1227
        """Return None or the location to push this branch to."""
 
1228
        return self.get_config_stack().get('push_location')
1206
1229
 
1207
1230
    def set_push_location(self, location):
1208
1231
        """Set a new push location for this branch."""
1293
1316
            if repository_policy is not None:
1294
1317
                repository_policy.configure_branch(result)
1295
1318
            self.copy_content_into(result, revision_id=revision_id)
1296
 
            master_branch = self.get_master_branch()
1297
 
            if master_branch is None:
 
1319
            master_url = self.get_bound_location()
 
1320
            if master_url is None:
1298
1321
                result.set_parent(self.bzrdir.root_transport.base)
1299
1322
            else:
1300
 
                result.set_parent(master_branch.bzrdir.root_transport.base)
 
1323
                result.set_parent(master_url)
1301
1324
        finally:
1302
1325
            result.unlock()
1303
1326
        return result
1377
1400
        # TODO: We should probably also check that self.revision_history
1378
1401
        # matches the repository for older branch formats.
1379
1402
        # If looking for the code that cross-checks repository parents against
1380
 
        # the iter_reverse_revision_history output, that is now a repository
 
1403
        # the Graph.iter_lefthand_ancestry output, that is now a repository
1381
1404
        # specific check.
1382
1405
        return result
1383
1406
 
1384
 
    def _get_checkout_format(self):
 
1407
    def _get_checkout_format(self, lightweight=False):
1385
1408
        """Return the most suitable metadir for a checkout of this branch.
1386
1409
        Weaves are used if this branch's repository uses weaves.
1387
1410
        """
1433
1456
        """
1434
1457
        t = transport.get_transport(to_location)
1435
1458
        t.ensure_base()
 
1459
        format = self._get_checkout_format(lightweight=lightweight)
1436
1460
        if lightweight:
1437
 
            format = self._get_checkout_format()
1438
1461
            checkout = format.initialize_on_transport(t)
1439
1462
            from_branch = BranchReferenceFormat().initialize(checkout, 
1440
1463
                target_branch=self)
1441
1464
        else:
1442
 
            format = self._get_checkout_format()
1443
 
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
 
1465
            checkout_branch = controldir.ControlDir.create_branch_convenience(
1444
1466
                to_location, force_new_tree=False, format=format)
1445
1467
            checkout = checkout_branch.bzrdir
1446
1468
            checkout_branch.bind(self)
1542
1564
            heads that must be fetched if present, but no error is necessary if
1543
1565
            they are not present.
1544
1566
        """
1545
 
        # For bzr native formats must_fetch is just the tip, and if_present_fetch
1546
 
        # are the tags.
 
1567
        # For bzr native formats must_fetch is just the tip, and
 
1568
        # if_present_fetch are the tags.
1547
1569
        must_fetch = set([self.last_revision()])
1548
 
        try:
1549
 
            if_present_fetch = set(self.tags.get_reverse_tag_dict())
1550
 
        except errors.TagsNotSupported:
1551
 
            if_present_fetch = set()
 
1570
        if_present_fetch = set()
 
1571
        if self.get_config_stack().get('branch.fetch_tags'):
 
1572
            try:
 
1573
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
 
1574
            except errors.TagsNotSupported:
 
1575
                pass
1552
1576
        must_fetch.discard(_mod_revision.NULL_REVISION)
1553
1577
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
1554
1578
        return must_fetch, if_present_fetch
1559
1583
 
1560
1584
    Formats provide three things:
1561
1585
     * An initialization routine,
1562
 
     * a format string,
 
1586
     * a format description
1563
1587
     * an open routine.
1564
1588
 
1565
1589
    Formats are placed in an dict by their format string for reference
1572
1596
    object will be created every time regardless.
1573
1597
    """
1574
1598
 
1575
 
    can_set_append_revisions_only = True
1576
 
 
1577
1599
    def __eq__(self, other):
1578
1600
        return self.__class__ is other.__class__
1579
1601
 
1581
1603
        return not (self == other)
1582
1604
 
1583
1605
    @classmethod
1584
 
    def find_format(klass, a_bzrdir, name=None):
1585
 
        """Return the format for the branch object in a_bzrdir."""
1586
 
        try:
1587
 
            transport = a_bzrdir.get_branch_transport(None, name=name)
1588
 
            format_string = transport.get_bytes("format")
1589
 
            return format_registry.get(format_string)
1590
 
        except errors.NoSuchFile:
1591
 
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1592
 
        except KeyError:
1593
 
            raise errors.UnknownFormatError(format=format_string, kind='branch')
1594
 
 
1595
 
    @classmethod
1596
1606
    @deprecated_method(deprecated_in((2, 4, 0)))
1597
1607
    def get_default_format(klass):
1598
1608
        """Return the current default format."""
1608
1618
        """
1609
1619
        return format_registry._get_all()
1610
1620
 
1611
 
    def get_reference(self, a_bzrdir, name=None):
1612
 
        """Get the target reference of the branch in a_bzrdir.
 
1621
    def get_reference(self, controldir, name=None):
 
1622
        """Get the target reference of the branch in controldir.
1613
1623
 
1614
1624
        format probing must have been completed before calling
1615
1625
        this method - it is assumed that the format of the branch
1616
 
        in a_bzrdir is correct.
 
1626
        in controldir is correct.
1617
1627
 
1618
 
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1628
        :param controldir: The controldir to get the branch data from.
1619
1629
        :param name: Name of the colocated branch to fetch
1620
1630
        :return: None if the branch is not a reference branch.
1621
1631
        """
1622
1632
        return None
1623
1633
 
1624
1634
    @classmethod
1625
 
    def set_reference(self, a_bzrdir, name, to_branch):
1626
 
        """Set the target reference of the branch in a_bzrdir.
 
1635
    def set_reference(self, controldir, name, to_branch):
 
1636
        """Set the target reference of the branch in controldir.
1627
1637
 
1628
1638
        format probing must have been completed before calling
1629
1639
        this method - it is assumed that the format of the branch
1630
 
        in a_bzrdir is correct.
 
1640
        in controldir is correct.
1631
1641
 
1632
 
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1642
        :param controldir: The controldir to set the branch reference for.
1633
1643
        :param name: Name of colocated branch to set, None for default
1634
1644
        :param to_branch: branch that the checkout is to reference
1635
1645
        """
1636
1646
        raise NotImplementedError(self.set_reference)
1637
1647
 
1638
 
    def get_format_string(self):
1639
 
        """Return the ASCII format string that identifies this format."""
1640
 
        raise NotImplementedError(self.get_format_string)
1641
 
 
1642
1648
    def get_format_description(self):
1643
1649
        """Return the short format description for this format."""
1644
1650
        raise NotImplementedError(self.get_format_description)
1645
1651
 
1646
 
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
 
1652
    def _run_post_branch_init_hooks(self, controldir, name, branch):
1647
1653
        hooks = Branch.hooks['post_branch_init']
1648
1654
        if not hooks:
1649
1655
            return
1650
 
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
 
1656
        params = BranchInitHookParams(self, controldir, name, branch)
1651
1657
        for hook in hooks:
1652
1658
            hook(params)
1653
1659
 
1654
 
    def initialize(self, a_bzrdir, name=None, repository=None):
1655
 
        """Create a branch of this format in a_bzrdir.
1656
 
        
 
1660
    def initialize(self, controldir, name=None, repository=None,
 
1661
                   append_revisions_only=None):
 
1662
        """Create a branch of this format in controldir.
 
1663
 
1657
1664
        :param name: Name of the colocated branch to create.
1658
1665
        """
1659
1666
        raise NotImplementedError(self.initialize)
1679
1686
        Note that it is normal for branch to be a RemoteBranch when using tags
1680
1687
        on a RemoteBranch.
1681
1688
        """
1682
 
        return DisabledTags(branch)
 
1689
        return _mod_tag.DisabledTags(branch)
1683
1690
 
1684
1691
    def network_name(self):
1685
1692
        """A simple byte string uniquely identifying this format for RPC calls.
1691
1698
        """
1692
1699
        raise NotImplementedError(self.network_name)
1693
1700
 
1694
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
1695
 
            found_repository=None):
1696
 
        """Return the branch object for a_bzrdir
 
1701
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
 
1702
            found_repository=None, possible_transports=None):
 
1703
        """Return the branch object for controldir.
1697
1704
 
1698
 
        :param a_bzrdir: A BzrDir that contains a branch.
 
1705
        :param controldir: A ControlDir that contains a branch.
1699
1706
        :param name: Name of colocated branch to open
1700
1707
        :param _found: a private parameter, do not use it. It is used to
1701
1708
            indicate if format probing has already be done.
1743
1750
        """True if this format supports tags stored in the branch"""
1744
1751
        return False  # by default
1745
1752
 
 
1753
    def tags_are_versioned(self):
 
1754
        """Whether the tag container for this branch versions tags."""
 
1755
        return False
 
1756
 
 
1757
    def supports_tags_referencing_ghosts(self):
 
1758
        """True if tags can reference ghost revisions."""
 
1759
        return True
 
1760
 
1746
1761
 
1747
1762
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1748
1763
    """A factory for a BranchFormat object, permitting simple lazy registration.
1762
1777
        """
1763
1778
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
1764
1779
        self._format_string = format_string
1765
 
        
 
1780
 
1766
1781
    def get_format_string(self):
1767
1782
        """See BranchFormat.get_format_string."""
1768
1783
        return self._format_string
1913
1928
    There are 4 fields that hooks may wish to access:
1914
1929
 
1915
1930
    :ivar format: the branch format
1916
 
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
 
1931
    :ivar bzrdir: the ControlDir where the branch will be/has been initialized
1917
1932
    :ivar name: name of colocated branch, if any (or None)
1918
1933
    :ivar branch: the branch created
1919
1934
 
1922
1937
    branch, which refer to the original branch.
1923
1938
    """
1924
1939
 
1925
 
    def __init__(self, format, a_bzrdir, name, branch):
 
1940
    def __init__(self, format, controldir, name, branch):
1926
1941
        """Create a group of BranchInitHook parameters.
1927
1942
 
1928
1943
        :param format: the branch format
1929
 
        :param a_bzrdir: the BzrDir where the branch will be/has been
 
1944
        :param controldir: the ControlDir where the branch will be/has been
1930
1945
            initialized
1931
1946
        :param name: name of colocated branch, if any (or None)
1932
1947
        :param branch: the branch created
1936
1951
        in branch, which refer to the original branch.
1937
1952
        """
1938
1953
        self.format = format
1939
 
        self.bzrdir = a_bzrdir
 
1954
        self.bzrdir = controldir
1940
1955
        self.name = name
1941
1956
        self.branch = branch
1942
1957
 
1952
1967
 
1953
1968
    There are 4 fields that hooks may wish to access:
1954
1969
 
1955
 
    :ivar control_dir: BzrDir of the checkout to change
 
1970
    :ivar control_dir: ControlDir of the checkout to change
1956
1971
    :ivar to_branch: branch that the checkout is to reference
1957
1972
    :ivar force: skip the check for local commits in a heavy checkout
1958
1973
    :ivar revision_id: revision ID to switch to (or None)
1961
1976
    def __init__(self, control_dir, to_branch, force, revision_id):
1962
1977
        """Create a group of SwitchHook parameters.
1963
1978
 
1964
 
        :param control_dir: BzrDir of the checkout to change
 
1979
        :param control_dir: ControlDir of the checkout to change
1965
1980
        :param to_branch: branch that the checkout is to reference
1966
1981
        :param force: skip the check for local commits in a heavy checkout
1967
1982
        :param revision_id: revision ID to switch to (or None)
1980
1995
            self.revision_id)
1981
1996
 
1982
1997
 
1983
 
class BranchFormatMetadir(BranchFormat):
1984
 
    """Common logic for meta-dir based branch formats."""
 
1998
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
 
1999
    """Base class for branch formats that live in meta directories.
 
2000
    """
 
2001
 
 
2002
    def __init__(self):
 
2003
        BranchFormat.__init__(self)
 
2004
        bzrdir.BzrFormat.__init__(self)
 
2005
 
 
2006
    @classmethod
 
2007
    def find_format(klass, controldir, name=None):
 
2008
        """Return the format for the branch object in controldir."""
 
2009
        try:
 
2010
            transport = controldir.get_branch_transport(None, name=name)
 
2011
        except errors.NoSuchFile:
 
2012
            raise errors.NotBranchError(path=name, bzrdir=controldir)
 
2013
        try:
 
2014
            format_string = transport.get_bytes("format")
 
2015
        except errors.NoSuchFile:
 
2016
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
 
2017
        return klass._find_format(format_registry, 'branch', format_string)
1985
2018
 
1986
2019
    def _branch_class(self):
1987
2020
        """What class to instantiate on open calls."""
1988
2021
        raise NotImplementedError(self._branch_class)
1989
2022
 
 
2023
    def _get_initial_config(self, append_revisions_only=None):
 
2024
        if append_revisions_only:
 
2025
            return "append_revisions_only = True\n"
 
2026
        else:
 
2027
            # Avoid writing anything if append_revisions_only is disabled,
 
2028
            # as that is the default.
 
2029
            return ""
 
2030
 
1990
2031
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1991
2032
                           repository=None):
1992
2033
        """Initialize a branch in a bzrdir, with specified files
2004
2045
        control_files.create_lock()
2005
2046
        control_files.lock_write()
2006
2047
        try:
2007
 
            utf8_files += [('format', self.get_format_string())]
 
2048
            utf8_files += [('format', self.as_string())]
2008
2049
            for (filename, content) in utf8_files:
2009
2050
                branch_transport.put_bytes(
2010
2051
                    filename, content,
2016
2057
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2017
2058
        return branch
2018
2059
 
2019
 
    def network_name(self):
2020
 
        """A simple byte string uniquely identifying this format for RPC calls.
2021
 
 
2022
 
        Metadir branch formats use their format string.
2023
 
        """
2024
 
        return self.get_format_string()
2025
 
 
2026
2060
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2027
 
            found_repository=None):
 
2061
            found_repository=None, possible_transports=None):
2028
2062
        """See BranchFormat.open()."""
2029
2063
        if not _found:
2030
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2064
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2031
2065
            if format.__class__ != self.__class__:
2032
2066
                raise AssertionError("wrong format %r found for %r" %
2033
2067
                    (format, self))
2042
2076
                              name=name,
2043
2077
                              a_bzrdir=a_bzrdir,
2044
2078
                              _repository=found_repository,
2045
 
                              ignore_fallbacks=ignore_fallbacks)
 
2079
                              ignore_fallbacks=ignore_fallbacks,
 
2080
                              possible_transports=possible_transports)
2046
2081
        except errors.NoSuchFile:
2047
2082
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2048
2083
 
2049
 
    def __init__(self):
2050
 
        super(BranchFormatMetadir, self).__init__()
2051
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2052
 
        self._matchingbzrdir.set_branch_format(self)
 
2084
    @property
 
2085
    def _matchingbzrdir(self):
 
2086
        ret = bzrdir.BzrDirMetaFormat1()
 
2087
        ret.set_branch_format(self)
 
2088
        return ret
2053
2089
 
2054
2090
    def supports_tags(self):
2055
2091
        return True
2057
2093
    def supports_leaving_lock(self):
2058
2094
        return True
2059
2095
 
 
2096
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
2097
            basedir=None):
 
2098
        BranchFormat.check_support_status(self,
 
2099
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
 
2100
            basedir=basedir)
 
2101
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
 
2102
            recommend_upgrade=recommend_upgrade, basedir=basedir)
 
2103
 
2060
2104
 
2061
2105
class BzrBranchFormat5(BranchFormatMetadir):
2062
2106
    """Bzr branch format 5.
2074
2118
    def _branch_class(self):
2075
2119
        return BzrBranch5
2076
2120
 
2077
 
    def get_format_string(self):
 
2121
    @classmethod
 
2122
    def get_format_string(cls):
2078
2123
        """See BranchFormat.get_format_string()."""
2079
2124
        return "Bazaar-NG branch format 5\n"
2080
2125
 
2082
2127
        """See BranchFormat.get_format_description()."""
2083
2128
        return "Branch format 5"
2084
2129
 
2085
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2130
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2131
                   append_revisions_only=None):
2086
2132
        """Create a branch of this format in a_bzrdir."""
 
2133
        if append_revisions_only:
 
2134
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2087
2135
        utf8_files = [('revision-history', ''),
2088
2136
                      ('branch-name', ''),
2089
2137
                      ]
2107
2155
    def _branch_class(self):
2108
2156
        return BzrBranch6
2109
2157
 
2110
 
    def get_format_string(self):
 
2158
    @classmethod
 
2159
    def get_format_string(cls):
2111
2160
        """See BranchFormat.get_format_string()."""
2112
2161
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
2113
2162
 
2115
2164
        """See BranchFormat.get_format_description()."""
2116
2165
        return "Branch format 6"
2117
2166
 
2118
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2167
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2168
                   append_revisions_only=None):
2119
2169
        """Create a branch of this format in a_bzrdir."""
2120
2170
        utf8_files = [('last-revision', '0 null:\n'),
2121
 
                      ('branch.conf', ''),
 
2171
                      ('branch.conf',
 
2172
                          self._get_initial_config(append_revisions_only)),
2122
2173
                      ('tags', ''),
2123
2174
                      ]
2124
2175
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2125
2176
 
2126
2177
    def make_tags(self, branch):
2127
2178
        """See bzrlib.branch.BranchFormat.make_tags()."""
2128
 
        return BasicTags(branch)
 
2179
        return _mod_tag.BasicTags(branch)
2129
2180
 
2130
2181
    def supports_set_append_revisions_only(self):
2131
2182
        return True
2137
2188
    def _branch_class(self):
2138
2189
        return BzrBranch8
2139
2190
 
2140
 
    def get_format_string(self):
 
2191
    @classmethod
 
2192
    def get_format_string(cls):
2141
2193
        """See BranchFormat.get_format_string()."""
2142
2194
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2143
2195
 
2145
2197
        """See BranchFormat.get_format_description()."""
2146
2198
        return "Branch format 8"
2147
2199
 
2148
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2200
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2201
                   append_revisions_only=None):
2149
2202
        """Create a branch of this format in a_bzrdir."""
2150
2203
        utf8_files = [('last-revision', '0 null:\n'),
2151
 
                      ('branch.conf', ''),
 
2204
                      ('branch.conf',
 
2205
                          self._get_initial_config(append_revisions_only)),
2152
2206
                      ('tags', ''),
2153
2207
                      ('references', '')
2154
2208
                      ]
2156
2210
 
2157
2211
    def make_tags(self, branch):
2158
2212
        """See bzrlib.branch.BranchFormat.make_tags()."""
2159
 
        return BasicTags(branch)
 
2213
        return _mod_tag.BasicTags(branch)
2160
2214
 
2161
2215
    def supports_set_append_revisions_only(self):
2162
2216
        return True
2176
2230
    This format was introduced in bzr 1.6.
2177
2231
    """
2178
2232
 
2179
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2233
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2234
                   append_revisions_only=None):
2180
2235
        """Create a branch of this format in a_bzrdir."""
2181
2236
        utf8_files = [('last-revision', '0 null:\n'),
2182
 
                      ('branch.conf', ''),
 
2237
                      ('branch.conf',
 
2238
                          self._get_initial_config(append_revisions_only)),
2183
2239
                      ('tags', ''),
2184
2240
                      ]
2185
2241
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2187
2243
    def _branch_class(self):
2188
2244
        return BzrBranch7
2189
2245
 
2190
 
    def get_format_string(self):
 
2246
    @classmethod
 
2247
    def get_format_string(cls):
2191
2248
        """See BranchFormat.get_format_string()."""
2192
2249
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2193
2250
 
2203
2260
 
2204
2261
    def make_tags(self, branch):
2205
2262
        """See bzrlib.branch.BranchFormat.make_tags()."""
2206
 
        return BasicTags(branch)
 
2263
        return _mod_tag.BasicTags(branch)
2207
2264
 
2208
2265
    supports_reference_locations = False
2209
2266
 
2210
2267
 
2211
 
class BranchReferenceFormat(BranchFormat):
 
2268
class BranchReferenceFormat(BranchFormatMetadir):
2212
2269
    """Bzr branch reference format.
2213
2270
 
2214
2271
    Branch references are used in implementing checkouts, they
2219
2276
     - a format string
2220
2277
    """
2221
2278
 
2222
 
    def get_format_string(self):
 
2279
    @classmethod
 
2280
    def get_format_string(cls):
2223
2281
        """See BranchFormat.get_format_string()."""
2224
2282
        return "Bazaar-NG Branch Reference Format 1\n"
2225
2283
 
2238
2296
        location = transport.put_bytes('location', to_branch.base)
2239
2297
 
2240
2298
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2241
 
            repository=None):
 
2299
            repository=None, append_revisions_only=None):
2242
2300
        """Create a branch of this format in a_bzrdir."""
2243
2301
        if target_branch is None:
2244
2302
            # this format does not implement branch itself, thus the implicit
2245
2303
            # creation contract must see it as uninitializable
2246
2304
            raise errors.UninitializableFormat(self)
2247
2305
        mutter('creating branch reference in %s', a_bzrdir.user_url)
 
2306
        if a_bzrdir._format.fixed_components:
 
2307
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
2248
2308
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2249
2309
        branch_transport.put_bytes('location',
2250
 
            target_branch.bzrdir.user_url)
2251
 
        branch_transport.put_bytes('format', self.get_format_string())
 
2310
            target_branch.user_url)
 
2311
        branch_transport.put_bytes('format', self.as_string())
2252
2312
        branch = self.open(
2253
2313
            a_bzrdir, name, _found=True,
2254
2314
            possible_transports=[target_branch.bzrdir.root_transport])
2255
2315
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2256
2316
        return branch
2257
2317
 
2258
 
    def __init__(self):
2259
 
        super(BranchReferenceFormat, self).__init__()
2260
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2261
 
        self._matchingbzrdir.set_branch_format(self)
2262
 
 
2263
2318
    def _make_reference_clone_function(format, a_branch):
2264
2319
        """Create a clone() routine for a branch dynamically."""
2265
2320
        def clone(to_bzrdir, revision_id=None,
2288
2343
        :param possible_transports: An optional reusable transports list.
2289
2344
        """
2290
2345
        if not _found:
2291
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2346
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2292
2347
            if format.__class__ != self.__class__:
2293
2348
                raise AssertionError("wrong format %r found for %r" %
2294
2349
                    (format, self))
2295
2350
        if location is None:
2296
2351
            location = self.get_reference(a_bzrdir, name)
2297
 
        real_bzrdir = bzrdir.BzrDir.open(
 
2352
        real_bzrdir = controldir.ControlDir.open(
2298
2353
            location, possible_transports=possible_transports)
2299
2354
        result = real_bzrdir.open_branch(name=name, 
2300
 
            ignore_fallbacks=ignore_fallbacks)
 
2355
            ignore_fallbacks=ignore_fallbacks,
 
2356
            possible_transports=possible_transports)
2301
2357
        # this changes the behaviour of result.clone to create a new reference
2302
2358
        # rather than a copy of the content of the branch.
2303
2359
        # I did not use a proxy object because that needs much more extensive
2384
2440
 
2385
2441
    def __init__(self, _format=None,
2386
2442
                 _control_files=None, a_bzrdir=None, name=None,
2387
 
                 _repository=None, ignore_fallbacks=False):
 
2443
                 _repository=None, ignore_fallbacks=False,
 
2444
                 possible_transports=None):
2388
2445
        """Create new branch object at a particular location."""
2389
2446
        if a_bzrdir is None:
2390
2447
            raise ValueError('a_bzrdir must be supplied')
2391
2448
        else:
2392
2449
            self.bzrdir = a_bzrdir
2393
 
        self._base = self.bzrdir.transport.clone('..').base
 
2450
        self._user_transport = self.bzrdir.transport.clone('..')
 
2451
        if name is not None:
 
2452
            self._user_transport.set_segment_parameter(
 
2453
                "branch", urlutils.escape(name))
 
2454
        self._base = self._user_transport.base
2394
2455
        self.name = name
2395
 
        # XXX: We should be able to just do
2396
 
        #   self.base = self.bzrdir.root_transport.base
2397
 
        # but this does not quite work yet -- mbp 20080522
2398
2456
        self._format = _format
2399
2457
        if _control_files is None:
2400
2458
            raise ValueError('BzrBranch _control_files is None')
2401
2459
        self.control_files = _control_files
2402
2460
        self._transport = _control_files._transport
2403
2461
        self.repository = _repository
2404
 
        Branch.__init__(self)
 
2462
        Branch.__init__(self, possible_transports)
2405
2463
 
2406
2464
    def __str__(self):
2407
 
        if self.name is None:
2408
 
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
2409
 
        else:
2410
 
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2411
 
                self.name)
 
2465
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
2412
2466
 
2413
2467
    __repr__ = __str__
2414
2468
 
2418
2472
 
2419
2473
    base = property(_get_base, doc="The URL for the root of this branch.")
2420
2474
 
 
2475
    @property
 
2476
    def user_transport(self):
 
2477
        return self._user_transport
 
2478
 
2421
2479
    def _get_config(self):
2422
 
        return TransportConfig(self._transport, 'branch.conf')
 
2480
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
 
2481
 
 
2482
    def _get_config_store(self):
 
2483
        return _mod_config.BranchStore(self)
2423
2484
 
2424
2485
    def is_locked(self):
2425
2486
        return self.control_files.is_locked()
2506
2567
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2507
2568
        revision_id = _mod_revision.ensure_null(revision_id)
2508
2569
        old_revno, old_revid = self.last_revision_info()
2509
 
        if self._get_append_revisions_only():
 
2570
        if self.get_append_revisions_only():
2510
2571
            self._check_history_violation(revision_id)
2511
2572
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2512
2573
        self._write_last_revision_info(revno, revision_id)
2655
2716
        self._transport.put_bytes('last-revision', out_string,
2656
2717
            mode=self.bzrdir._get_file_mode())
2657
2718
 
 
2719
    @needs_write_lock
 
2720
    def update_feature_flags(self, updated_flags):
 
2721
        """Update the feature flags for this branch.
 
2722
 
 
2723
        :param updated_flags: Dictionary mapping feature names to necessities
 
2724
            A necessity can be None to indicate the feature should be removed
 
2725
        """
 
2726
        self._format._update_feature_flags(updated_flags)
 
2727
        self.control_transport.put_bytes('format', self._format.as_string())
 
2728
 
2658
2729
 
2659
2730
class FullHistoryBzrBranch(BzrBranch):
2660
2731
    """Bzr branch which contains the full revision history."""
2673
2744
        self._set_revision_history(history)
2674
2745
 
2675
2746
    def _read_last_revision_info(self):
2676
 
        rh = self.revision_history()
 
2747
        rh = self._revision_history()
2677
2748
        revno = len(rh)
2678
2749
        if revno:
2679
2750
            return (revno, rh[-1])
2733
2804
        if revision_id == _mod_revision.NULL_REVISION:
2734
2805
            new_history = []
2735
2806
        else:
2736
 
            new_history = self.revision_history()
 
2807
            new_history = self._revision_history()
2737
2808
        if revision_id is not None and new_history != []:
2738
2809
            try:
2739
2810
                new_history = new_history[:new_history.index(revision_id) + 1]
2767
2838
class BzrBranch8(BzrBranch):
2768
2839
    """A branch that stores tree-reference locations."""
2769
2840
 
2770
 
    def _open_hook(self):
 
2841
    def _open_hook(self, possible_transports=None):
2771
2842
        if self._ignore_fallbacks:
2772
2843
            return
 
2844
        if possible_transports is None:
 
2845
            possible_transports = [self.bzrdir.root_transport]
2773
2846
        try:
2774
2847
            url = self.get_stacked_on_url()
2775
2848
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2783
2856
                    raise AssertionError(
2784
2857
                        "'transform_fallback_location' hook %s returned "
2785
2858
                        "None, not a URL." % hook_name)
2786
 
            self._activate_fallback_location(url)
 
2859
            self._activate_fallback_location(url,
 
2860
                possible_transports=possible_transports)
2787
2861
 
2788
2862
    def __init__(self, *args, **kwargs):
2789
2863
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2907
2981
        """See Branch.set_push_location."""
2908
2982
        self._master_branch_cache = None
2909
2983
        result = None
2910
 
        config = self.get_config()
 
2984
        conf = self.get_config_stack()
2911
2985
        if location is None:
2912
 
            if config.get_user_option('bound') != 'True':
 
2986
            if not conf.get('bound'):
2913
2987
                return False
2914
2988
            else:
2915
 
                config.set_user_option('bound', 'False', warn_masked=True)
 
2989
                conf.set('bound', 'False')
2916
2990
                return True
2917
2991
        else:
2918
2992
            self._set_config_location('bound_location', location,
2919
 
                                      config=config)
2920
 
            config.set_user_option('bound', 'True', warn_masked=True)
 
2993
                                      config=conf)
 
2994
            conf.set('bound', 'True')
2921
2995
        return True
2922
2996
 
2923
2997
    def _get_bound_location(self, bound):
2924
2998
        """Return the bound location in the config file.
2925
2999
 
2926
3000
        Return None if the bound parameter does not match"""
2927
 
        config = self.get_config()
2928
 
        config_bound = (config.get_user_option('bound') == 'True')
2929
 
        if config_bound != bound:
 
3001
        conf = self.get_config_stack()
 
3002
        if conf.get('bound') != bound:
2930
3003
            return None
2931
 
        return self._get_config_location('bound_location', config=config)
 
3004
        return self._get_config_location('bound_location', config=conf)
2932
3005
 
2933
3006
    def get_bound_location(self):
2934
 
        """See Branch.set_push_location."""
 
3007
        """See Branch.get_bound_location."""
2935
3008
        return self._get_bound_location(True)
2936
3009
 
2937
3010
    def get_old_bound_location(self):
2942
3015
        # you can always ask for the URL; but you might not be able to use it
2943
3016
        # if the repo can't support stacking.
2944
3017
        ## self._check_stackable_repo()
2945
 
        stacked_url = self._get_config_location('stacked_on_location')
 
3018
        # stacked_on_location is only ever defined in branch.conf, so don't
 
3019
        # waste effort reading the whole stack of config files.
 
3020
        conf = _mod_config.BranchOnlyStack(self)
 
3021
        stacked_url = self._get_config_location('stacked_on_location',
 
3022
                                                config=conf)
2946
3023
        if stacked_url is None:
2947
3024
            raise errors.NotStacked(self)
2948
 
        return stacked_url
2949
 
 
2950
 
    def _get_append_revisions_only(self):
2951
 
        return self.get_config(
2952
 
            ).get_user_option_as_bool('append_revisions_only')
 
3025
        return stacked_url.encode('utf-8')
2953
3026
 
2954
3027
    @needs_read_lock
2955
3028
    def get_rev_id(self, revno, history=None):
2985
3058
            except errors.RevisionNotPresent, e:
2986
3059
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
2987
3060
            index = len(self._partial_revision_history_cache) - 1
 
3061
            if index < 0:
 
3062
                raise errors.NoSuchRevision(self, revision_id)
2988
3063
            if self._partial_revision_history_cache[index] != revision_id:
2989
3064
                raise errors.NoSuchRevision(self, revision_id)
2990
3065
        return self.revno() - index
3042
3117
    :ivar local_branch: target branch if there is a Master, else None
3043
3118
    :ivar target_branch: Target/destination branch object. (write locked)
3044
3119
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
 
3120
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3045
3121
    """
3046
3122
 
3047
3123
    @deprecated_method(deprecated_in((2, 3, 0)))
3053
3129
        return self.new_revno - self.old_revno
3054
3130
 
3055
3131
    def report(self, to_file):
 
3132
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
3133
        tag_updates = getattr(self, "tag_updates", None)
3056
3134
        if not is_quiet():
3057
 
            if self.old_revid == self.new_revid:
3058
 
                to_file.write('No revisions to pull.\n')
3059
 
            else:
 
3135
            if self.old_revid != self.new_revid:
3060
3136
                to_file.write('Now on revision %d.\n' % self.new_revno)
 
3137
            if tag_updates:
 
3138
                to_file.write('%d tag(s) updated.\n' % len(tag_updates))
 
3139
            if self.old_revid == self.new_revid and not tag_updates:
 
3140
                if not tag_conflicts:
 
3141
                    to_file.write('No revisions or tags to pull.\n')
 
3142
                else:
 
3143
                    to_file.write('No revisions to pull.\n')
3061
3144
        self._show_tag_conficts(to_file)
3062
3145
 
3063
3146
 
3089
3172
        return self.new_revno - self.old_revno
3090
3173
 
3091
3174
    def report(self, to_file):
3092
 
        """Write a human-readable description of the result."""
3093
 
        if self.old_revid == self.new_revid:
3094
 
            note('No new revisions to push.')
3095
 
        else:
3096
 
            note('Pushed up to revision %d.' % self.new_revno)
 
3175
        # TODO: This function gets passed a to_file, but then
 
3176
        # ignores it and calls note() instead. This is also
 
3177
        # inconsistent with PullResult(), which writes to stdout.
 
3178
        # -- JRV20110901, bug #838853
 
3179
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
3180
        tag_updates = getattr(self, "tag_updates", None)
 
3181
        if not is_quiet():
 
3182
            if self.old_revid != self.new_revid:
 
3183
                note(gettext('Pushed up to revision %d.') % self.new_revno)
 
3184
            if tag_updates:
 
3185
                note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
 
3186
            if self.old_revid == self.new_revid and not tag_updates:
 
3187
                if not tag_conflicts:
 
3188
                    note(gettext('No new revisions or tags to push.'))
 
3189
                else:
 
3190
                    note(gettext('No new revisions to push.'))
3097
3191
        self._show_tag_conficts(to_file)
3098
3192
 
3099
3193
 
3113
3207
        :param verbose: Requests more detailed display of what was checked,
3114
3208
            if any.
3115
3209
        """
3116
 
        note('checked branch %s format %s', self.branch.user_url,
3117
 
            self.branch._format)
 
3210
        note(gettext('checked branch {0} format {1}').format(
 
3211
                                self.branch.user_url, self.branch._format))
3118
3212
        for error in self.errors:
3119
 
            note('found error:%s', error)
 
3213
            note(gettext('found error:%s'), error)
3120
3214
 
3121
3215
 
3122
3216
class Converter5to6(object):
3138
3232
 
3139
3233
        # Copying done; now update target format
3140
3234
        new_branch._transport.put_bytes('format',
3141
 
            format.get_format_string(),
 
3235
            format.as_string(),
3142
3236
            mode=new_branch.bzrdir._get_file_mode())
3143
3237
 
3144
3238
        # Clean up old files
3157
3251
        format = BzrBranchFormat7()
3158
3252
        branch._set_config_location('stacked_on_location', '')
3159
3253
        # update target format
3160
 
        branch._transport.put_bytes('format', format.get_format_string())
 
3254
        branch._transport.put_bytes('format', format.as_string())
3161
3255
 
3162
3256
 
3163
3257
class Converter7to8(object):
3164
 
    """Perform an in-place upgrade of format 6 to format 7"""
 
3258
    """Perform an in-place upgrade of format 7 to format 8"""
3165
3259
 
3166
3260
    def convert(self, branch):
3167
3261
        format = BzrBranchFormat8()
3168
3262
        branch._transport.put_bytes('references', '')
3169
3263
        # update target format
3170
 
        branch._transport.put_bytes('format', format.get_format_string())
 
3264
        branch._transport.put_bytes('format', format.as_string())
3171
3265
 
3172
3266
 
3173
3267
class InterBranch(InterObject):
3340
3434
        if local and not bound_location:
3341
3435
            raise errors.LocalRequiresBoundBranch()
3342
3436
        master_branch = None
3343
 
        source_is_master = (self.source.user_url == bound_location)
 
3437
        source_is_master = False
 
3438
        if bound_location:
 
3439
            # bound_location comes from a config file, some care has to be
 
3440
            # taken to relate it to source.user_url
 
3441
            normalized = urlutils.normalize_url(bound_location)
 
3442
            try:
 
3443
                relpath = self.source.user_transport.relpath(normalized)
 
3444
                source_is_master = (relpath == '')
 
3445
            except (errors.PathNotChild, errors.InvalidURL):
 
3446
                source_is_master = False
3344
3447
        if not local and bound_location and not source_is_master:
3345
3448
            # not pulling from master, so we need to update master.
3346
3449
            master_branch = self.target.get_master_branch(possible_transports)
3398
3501
            self._update_revisions(stop_revision, overwrite=overwrite,
3399
3502
                    graph=graph)
3400
3503
        if self.source._push_should_merge_tags():
3401
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3402
 
                overwrite)
 
3504
            result.tag_updates, result.tag_conflicts = (
 
3505
                self.source.tags.merge_to(self.target.tags, overwrite))
3403
3506
        result.new_revno, result.new_revid = self.target.last_revision_info()
3404
3507
        return result
3405
3508
 
3488
3591
            # TODO: The old revid should be specified when merging tags, 
3489
3592
            # so a tags implementation that versions tags can only 
3490
3593
            # pull in the most recent changes. -- JRV20090506
3491
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3492
 
                overwrite, ignore_master=not merge_tags_to_master)
 
3594
            result.tag_updates, result.tag_conflicts = (
 
3595
                self.source.tags.merge_to(self.target.tags, overwrite,
 
3596
                    ignore_master=not merge_tags_to_master))
3493
3597
            result.new_revno, result.new_revid = self.target.last_revision_info()
3494
3598
            if _hook_master:
3495
3599
                result.master_branch = _hook_master