~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Martin Packman
  • Date: 2012-01-05 10:37:58 UTC
  • mto: This revision was merged to the branch mainline in revision 6427.
  • Revision ID: martin.packman@canonical.com-20120105103758-wzftnmsip5iv9n2g
Revert addition of get_message_encoding function

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
import bzrlib.bzrdir
17
18
 
18
19
from cStringIO import StringIO
19
 
import sys
20
20
 
21
21
from bzrlib.lazy_import import lazy_import
22
22
lazy_import(globals(), """
23
 
from itertools import chain
 
23
import itertools
24
24
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,
 
25
    bzrdir,
 
26
    controldir,
 
27
    cache_utf8,
 
28
    cleanup,
 
29
    config as _mod_config,
 
30
    debug,
 
31
    errors,
 
32
    fetch,
 
33
    graph as _mod_graph,
 
34
    lockdir,
 
35
    lockable_files,
 
36
    remote,
 
37
    repository,
 
38
    revision as _mod_revision,
 
39
    rio,
 
40
    tag as _mod_tag,
 
41
    transport,
 
42
    ui,
 
43
    urlutils,
 
44
    vf_search,
47
45
    )
 
46
from bzrlib.i18n import gettext, ngettext
48
47
""")
49
48
 
50
49
from bzrlib import (
66
65
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
67
66
 
68
67
 
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
68
class Branch(controldir.ControlComponent):
75
69
    """Branch holding a history of revisions.
76
70
 
93
87
    def user_transport(self):
94
88
        return self.bzrdir.user_transport
95
89
 
96
 
    def __init__(self, *ignored, **ignored_too):
 
90
    def __init__(self, possible_transports=None):
97
91
        self.tags = self._format.make_tags(self)
98
92
        self._revision_history_cache = None
99
93
        self._revision_id_to_revno_cache = None
103
97
        self._last_revision_info_cache = None
104
98
        self._master_branch_cache = None
105
99
        self._merge_sorted_revisions_cache = None
106
 
        self._open_hook()
 
100
        self._open_hook(possible_transports)
107
101
        hooks = Branch.hooks['open']
108
102
        for hook in hooks:
109
103
            hook(self)
110
104
 
111
 
    def _open_hook(self):
 
105
    def _open_hook(self, possible_transports):
112
106
        """Called by init to allow simpler extension of the base class."""
113
107
 
114
 
    def _activate_fallback_location(self, url):
 
108
    def _activate_fallback_location(self, url, possible_transports):
115
109
        """Activate the branch/repository from url as a fallback repository."""
116
110
        for existing_fallback_repo in self.repository._fallback_repositories:
117
111
            if existing_fallback_repo.user_url == url:
118
112
                # This fallback is already configured.  This probably only
119
 
                # happens because BzrDir.sprout is a horrible mess.  To avoid
 
113
                # happens because ControlDir.sprout is a horrible mess.  To avoid
120
114
                # confusing _unstack we don't add this a second time.
121
115
                mutter('duplicate activation of fallback %r on %r', url, self)
122
116
                return
123
 
        repo = self._get_fallback_repository(url)
 
117
        repo = self._get_fallback_repository(url, possible_transports)
124
118
        if repo.has_same_location(self.repository):
125
119
            raise errors.UnstackableLocationError(self.user_url, url)
126
120
        self.repository.add_fallback_repository(repo)
180
174
        For instance, if the branch is at URL/.bzr/branch,
181
175
        Branch.open(URL) -> a Branch instance.
182
176
        """
183
 
        control = bzrdir.BzrDir.open(base, _unsupported,
 
177
        control = controldir.ControlDir.open(base, _unsupported,
184
178
                                     possible_transports=possible_transports)
185
 
        return control.open_branch(unsupported=_unsupported)
 
179
        return control.open_branch(unsupported=_unsupported,
 
180
            possible_transports=possible_transports)
186
181
 
187
182
    @staticmethod
188
 
    def open_from_transport(transport, name=None, _unsupported=False):
 
183
    def open_from_transport(transport, name=None, _unsupported=False,
 
184
            possible_transports=None):
189
185
        """Open the branch rooted at transport"""
190
 
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
191
 
        return control.open_branch(name=name, unsupported=_unsupported)
 
186
        control = controldir.ControlDir.open_from_transport(transport, _unsupported)
 
187
        return control.open_branch(name=name, unsupported=_unsupported,
 
188
            possible_transports=possible_transports)
192
189
 
193
190
    @staticmethod
194
191
    def open_containing(url, possible_transports=None):
202
199
        format, UnknownFormatError or UnsupportedFormatError are raised.
203
200
        If there is one, it is returned, along with the unused portion of url.
204
201
        """
205
 
        control, relpath = bzrdir.BzrDir.open_containing(url,
 
202
        control, relpath = controldir.ControlDir.open_containing(url,
206
203
                                                         possible_transports)
207
 
        return control.open_branch(), relpath
 
204
        branch = control.open_branch(possible_transports=possible_transports)
 
205
        return (branch, relpath)
208
206
 
209
207
    def _push_should_merge_tags(self):
210
208
        """Should _basic_push merge this branch's tags into the target?
222
220
 
223
221
        :return: A bzrlib.config.BranchConfig.
224
222
        """
225
 
        return BranchConfig(self)
 
223
        return _mod_config.BranchConfig(self)
 
224
 
 
225
    def get_config_stack(self):
 
226
        """Get a bzrlib.config.BranchStack for this Branch.
 
227
 
 
228
        This can then be used to get and set configuration options for the
 
229
        branch.
 
230
 
 
231
        :return: A bzrlib.config.BranchStack.
 
232
        """
 
233
        return _mod_config.BranchStack(self)
226
234
 
227
235
    def _get_config(self):
228
236
        """Get the concrete config for just the config in this branch.
236
244
        """
237
245
        raise NotImplementedError(self._get_config)
238
246
 
239
 
    def _get_fallback_repository(self, url):
 
247
    def _get_fallback_repository(self, url, possible_transports):
240
248
        """Get the repository we fallback to at url."""
241
249
        url = urlutils.join(self.base, url)
242
 
        a_branch = Branch.open(url,
243
 
            possible_transports=[self.bzrdir.root_transport])
 
250
        a_branch = Branch.open(url, possible_transports=possible_transports)
244
251
        return a_branch.repository
245
252
 
246
253
    @needs_read_lock
520
527
                    # The decision to include the start or not
521
528
                    # depends on the stop_rule if a stop is provided
522
529
                    # so pop this node back into the iterator
523
 
                    rev_iter = chain(iter([node]), rev_iter)
 
530
                    rev_iter = itertools.chain(iter([node]), rev_iter)
524
531
                    break
525
532
        if stop_revision_id is None:
526
533
            # Yield everything
651
658
        """
652
659
        raise errors.UpgradeRequired(self.user_url)
653
660
 
 
661
    def get_append_revisions_only(self):
 
662
        """Whether it is only possible to append revisions to the history.
 
663
        """
 
664
        if not self._format.supports_set_append_revisions_only():
 
665
            return False
 
666
        return self.get_config_stack().get('append_revisions_only')
 
667
 
654
668
    def set_append_revisions_only(self, enabled):
655
669
        if not self._format.supports_set_append_revisions_only():
656
670
            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)
 
671
        self.get_config_stack().set('append_revisions_only', enabled)
663
672
 
664
673
    def set_reference_info(self, file_id, tree_path, branch_location):
665
674
        """Set the branch location to use for a tree reference."""
694
703
        """
695
704
        raise errors.UpgradeRequired(self.user_url)
696
705
 
697
 
    def get_commit_builder(self, parents, config=None, timestamp=None,
 
706
    def get_commit_builder(self, parents, config_stack=None, timestamp=None,
698
707
                           timezone=None, committer=None, revprops=None,
699
708
                           revision_id=None, lossy=False):
700
709
        """Obtain a CommitBuilder for this branch.
710
719
            represented, when pushing to a foreign VCS 
711
720
        """
712
721
 
713
 
        if config is None:
714
 
            config = self.get_config()
 
722
        if config_stack is None:
 
723
            config_stack = self.get_config_stack()
715
724
 
716
 
        return self.repository.get_commit_builder(self, parents, config,
 
725
        return self.repository.get_commit_builder(self, parents, config_stack,
717
726
            timestamp, timezone, committer, revprops, revision_id,
718
727
            lossy)
719
728
 
724
733
        """
725
734
        return None
726
735
 
 
736
    @deprecated_method(deprecated_in((2, 5, 0)))
727
737
    def get_revision_delta(self, revno):
728
738
        """Return the delta for one revision.
729
739
 
730
740
        The delta is relative to its mainline predecessor, or the
731
741
        empty tree for revision 1.
732
742
        """
733
 
        rh = self.revision_history()
734
 
        if not (1 <= revno <= len(rh)):
 
743
        try:
 
744
            revid = self.get_rev_id(revno)
 
745
        except errors.NoSuchRevision:
735
746
            raise errors.InvalidRevisionNumber(revno)
736
 
        return self.repository.get_revision_delta(rh[revno-1])
 
747
        return self.repository.get_revision_delta(revid)
737
748
 
738
749
    def get_stacked_on_url(self):
739
750
        """Get the URL this branch is stacked against.
785
796
                                  other_branch=None):
786
797
        """See Branch.generate_revision_history"""
787
798
        graph = self.repository.get_graph()
 
799
        (last_revno, last_revid) = self.last_revision_info()
788
800
        known_revision_ids = [
789
 
            self.last_revision_info(),
 
801
            (last_revid, last_revno),
790
802
            (_mod_revision.NULL_REVISION, 0),
791
803
            ]
792
804
        if last_rev is not None:
837
849
                return
838
850
            self._unstack()
839
851
        else:
840
 
            self._activate_fallback_location(url)
 
852
            self._activate_fallback_location(url,
 
853
                possible_transports=[self.bzrdir.root_transport])
841
854
        # write this out after the repository is stacked to avoid setting a
842
855
        # stacked config that doesn't work.
843
856
        self._set_config_location('stacked_on_location', url)
849
862
        """
850
863
        pb = ui.ui_factory.nested_progress_bar()
851
864
        try:
852
 
            pb.update("Unstacking")
 
865
            pb.update(gettext("Unstacking"))
853
866
            # The basic approach here is to fetch the tip of the branch,
854
867
            # including all available ghosts, from the existing stacked
855
868
            # repository into a new repository object without the fallbacks. 
869
882
            # stream from one of them to the other.  This does mean doing
870
883
            # separate SSH connection setup, but unstacking is not a
871
884
            # common operation so it's tolerable.
872
 
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
 
885
            new_bzrdir = controldir.ControlDir.open(
 
886
                self.bzrdir.root_transport.base)
873
887
            new_repository = new_bzrdir.find_repository()
874
888
            if new_repository._fallback_repositories:
875
889
                raise AssertionError("didn't expect %r to have "
918
932
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
919
933
                except errors.TagsNotSupported:
920
934
                    tags_to_fetch = set()
921
 
                fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
 
935
                fetch_spec = vf_search.NotInOtherForRevs(self.repository,
922
936
                    old_repository, required_ids=[self.last_revision()],
923
937
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
924
938
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
992
1006
        """
993
1007
        raise NotImplementedError(self._gen_revision_history)
994
1008
 
 
1009
    @deprecated_method(deprecated_in((2, 5, 0)))
995
1010
    @needs_read_lock
996
1011
    def revision_history(self):
997
1012
        """Return sequence of revision ids on this branch.
999
1014
        This method will cache the revision history for as long as it is safe to
1000
1015
        do so.
1001
1016
        """
 
1017
        return self._revision_history()
 
1018
 
 
1019
    def _revision_history(self):
1002
1020
        if 'evil' in debug.debug_flags:
1003
1021
            mutter_callsite(3, "revision_history scales with history.")
1004
1022
        if self._revision_history_cache is not None:
1074
1092
        """Given a revision id, return its revno"""
1075
1093
        if _mod_revision.is_null(revision_id):
1076
1094
            return 0
1077
 
        history = self.revision_history()
 
1095
        history = self._revision_history()
1078
1096
        try:
1079
1097
            return history.index(revision_id) + 1
1080
1098
        except ValueError:
1293
1311
            if repository_policy is not None:
1294
1312
                repository_policy.configure_branch(result)
1295
1313
            self.copy_content_into(result, revision_id=revision_id)
1296
 
            master_branch = self.get_master_branch()
1297
 
            if master_branch is None:
 
1314
            master_url = self.get_bound_location()
 
1315
            if master_url is None:
1298
1316
                result.set_parent(self.bzrdir.root_transport.base)
1299
1317
            else:
1300
 
                result.set_parent(master_branch.bzrdir.root_transport.base)
 
1318
                result.set_parent(master_url)
1301
1319
        finally:
1302
1320
            result.unlock()
1303
1321
        return result
1377
1395
        # TODO: We should probably also check that self.revision_history
1378
1396
        # matches the repository for older branch formats.
1379
1397
        # If looking for the code that cross-checks repository parents against
1380
 
        # the iter_reverse_revision_history output, that is now a repository
 
1398
        # the Graph.iter_lefthand_ancestry output, that is now a repository
1381
1399
        # specific check.
1382
1400
        return result
1383
1401
 
1384
 
    def _get_checkout_format(self):
 
1402
    def _get_checkout_format(self, lightweight=False):
1385
1403
        """Return the most suitable metadir for a checkout of this branch.
1386
1404
        Weaves are used if this branch's repository uses weaves.
1387
1405
        """
1433
1451
        """
1434
1452
        t = transport.get_transport(to_location)
1435
1453
        t.ensure_base()
 
1454
        format = self._get_checkout_format(lightweight=lightweight)
1436
1455
        if lightweight:
1437
 
            format = self._get_checkout_format()
1438
1456
            checkout = format.initialize_on_transport(t)
1439
1457
            from_branch = BranchReferenceFormat().initialize(checkout, 
1440
1458
                target_branch=self)
1441
1459
        else:
1442
 
            format = self._get_checkout_format()
1443
 
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
 
1460
            checkout_branch = controldir.ControlDir.create_branch_convenience(
1444
1461
                to_location, force_new_tree=False, format=format)
1445
1462
            checkout = checkout_branch.bzrdir
1446
1463
            checkout_branch.bind(self)
1545
1562
        # For bzr native formats must_fetch is just the tip, and if_present_fetch
1546
1563
        # are the tags.
1547
1564
        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()
 
1565
        if_present_fetch = set()
 
1566
        c = self.get_config()
 
1567
        include_tags = c.get_user_option_as_bool('branch.fetch_tags',
 
1568
                                                 default=False)
 
1569
        if include_tags:
 
1570
            try:
 
1571
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
 
1572
            except errors.TagsNotSupported:
 
1573
                pass
1552
1574
        must_fetch.discard(_mod_revision.NULL_REVISION)
1553
1575
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
1554
1576
        return must_fetch, if_present_fetch
1572
1594
    object will be created every time regardless.
1573
1595
    """
1574
1596
 
1575
 
    can_set_append_revisions_only = True
1576
 
 
1577
1597
    def __eq__(self, other):
1578
1598
        return self.__class__ is other.__class__
1579
1599
 
1581
1601
        return not (self == other)
1582
1602
 
1583
1603
    @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
1604
    @deprecated_method(deprecated_in((2, 4, 0)))
1597
1605
    def get_default_format(klass):
1598
1606
        """Return the current default format."""
1608
1616
        """
1609
1617
        return format_registry._get_all()
1610
1618
 
1611
 
    def get_reference(self, a_bzrdir, name=None):
1612
 
        """Get the target reference of the branch in a_bzrdir.
 
1619
    def get_reference(self, controldir, name=None):
 
1620
        """Get the target reference of the branch in controldir.
1613
1621
 
1614
1622
        format probing must have been completed before calling
1615
1623
        this method - it is assumed that the format of the branch
1616
 
        in a_bzrdir is correct.
 
1624
        in controldir is correct.
1617
1625
 
1618
 
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1626
        :param controldir: The controldir to get the branch data from.
1619
1627
        :param name: Name of the colocated branch to fetch
1620
1628
        :return: None if the branch is not a reference branch.
1621
1629
        """
1622
1630
        return None
1623
1631
 
1624
1632
    @classmethod
1625
 
    def set_reference(self, a_bzrdir, name, to_branch):
1626
 
        """Set the target reference of the branch in a_bzrdir.
 
1633
    def set_reference(self, controldir, name, to_branch):
 
1634
        """Set the target reference of the branch in controldir.
1627
1635
 
1628
1636
        format probing must have been completed before calling
1629
1637
        this method - it is assumed that the format of the branch
1630
 
        in a_bzrdir is correct.
 
1638
        in controldir is correct.
1631
1639
 
1632
 
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1640
        :param controldir: The controldir to set the branch reference for.
1633
1641
        :param name: Name of colocated branch to set, None for default
1634
1642
        :param to_branch: branch that the checkout is to reference
1635
1643
        """
1636
1644
        raise NotImplementedError(self.set_reference)
1637
1645
 
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
1646
    def get_format_description(self):
1643
1647
        """Return the short format description for this format."""
1644
1648
        raise NotImplementedError(self.get_format_description)
1645
1649
 
1646
 
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
 
1650
    def _run_post_branch_init_hooks(self, controldir, name, branch):
1647
1651
        hooks = Branch.hooks['post_branch_init']
1648
1652
        if not hooks:
1649
1653
            return
1650
 
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
 
1654
        params = BranchInitHookParams(self, controldir, name, branch)
1651
1655
        for hook in hooks:
1652
1656
            hook(params)
1653
1657
 
1654
 
    def initialize(self, a_bzrdir, name=None, repository=None):
1655
 
        """Create a branch of this format in a_bzrdir.
1656
 
        
 
1658
    def initialize(self, controldir, name=None, repository=None,
 
1659
                   append_revisions_only=None):
 
1660
        """Create a branch of this format in controldir.
 
1661
 
1657
1662
        :param name: Name of the colocated branch to create.
1658
1663
        """
1659
1664
        raise NotImplementedError(self.initialize)
1679
1684
        Note that it is normal for branch to be a RemoteBranch when using tags
1680
1685
        on a RemoteBranch.
1681
1686
        """
1682
 
        return DisabledTags(branch)
 
1687
        return _mod_tag.DisabledTags(branch)
1683
1688
 
1684
1689
    def network_name(self):
1685
1690
        """A simple byte string uniquely identifying this format for RPC calls.
1691
1696
        """
1692
1697
        raise NotImplementedError(self.network_name)
1693
1698
 
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
 
1699
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
 
1700
            found_repository=None, possible_transports=None):
 
1701
        """Return the branch object for controldir.
1697
1702
 
1698
 
        :param a_bzrdir: A BzrDir that contains a branch.
 
1703
        :param controldir: A ControlDir that contains a branch.
1699
1704
        :param name: Name of colocated branch to open
1700
1705
        :param _found: a private parameter, do not use it. It is used to
1701
1706
            indicate if format probing has already be done.
1743
1748
        """True if this format supports tags stored in the branch"""
1744
1749
        return False  # by default
1745
1750
 
 
1751
    def tags_are_versioned(self):
 
1752
        """Whether the tag container for this branch versions tags."""
 
1753
        return False
 
1754
 
 
1755
    def supports_tags_referencing_ghosts(self):
 
1756
        """True if tags can reference ghost revisions."""
 
1757
        return True
 
1758
 
1746
1759
 
1747
1760
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1748
1761
    """A factory for a BranchFormat object, permitting simple lazy registration.
1762
1775
        """
1763
1776
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
1764
1777
        self._format_string = format_string
1765
 
        
 
1778
 
1766
1779
    def get_format_string(self):
1767
1780
        """See BranchFormat.get_format_string."""
1768
1781
        return self._format_string
1913
1926
    There are 4 fields that hooks may wish to access:
1914
1927
 
1915
1928
    :ivar format: the branch format
1916
 
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
 
1929
    :ivar bzrdir: the ControlDir where the branch will be/has been initialized
1917
1930
    :ivar name: name of colocated branch, if any (or None)
1918
1931
    :ivar branch: the branch created
1919
1932
 
1922
1935
    branch, which refer to the original branch.
1923
1936
    """
1924
1937
 
1925
 
    def __init__(self, format, a_bzrdir, name, branch):
 
1938
    def __init__(self, format, controldir, name, branch):
1926
1939
        """Create a group of BranchInitHook parameters.
1927
1940
 
1928
1941
        :param format: the branch format
1929
 
        :param a_bzrdir: the BzrDir where the branch will be/has been
 
1942
        :param controldir: the ControlDir where the branch will be/has been
1930
1943
            initialized
1931
1944
        :param name: name of colocated branch, if any (or None)
1932
1945
        :param branch: the branch created
1936
1949
        in branch, which refer to the original branch.
1937
1950
        """
1938
1951
        self.format = format
1939
 
        self.bzrdir = a_bzrdir
 
1952
        self.bzrdir = controldir
1940
1953
        self.name = name
1941
1954
        self.branch = branch
1942
1955
 
1952
1965
 
1953
1966
    There are 4 fields that hooks may wish to access:
1954
1967
 
1955
 
    :ivar control_dir: BzrDir of the checkout to change
 
1968
    :ivar control_dir: ControlDir of the checkout to change
1956
1969
    :ivar to_branch: branch that the checkout is to reference
1957
1970
    :ivar force: skip the check for local commits in a heavy checkout
1958
1971
    :ivar revision_id: revision ID to switch to (or None)
1961
1974
    def __init__(self, control_dir, to_branch, force, revision_id):
1962
1975
        """Create a group of SwitchHook parameters.
1963
1976
 
1964
 
        :param control_dir: BzrDir of the checkout to change
 
1977
        :param control_dir: ControlDir of the checkout to change
1965
1978
        :param to_branch: branch that the checkout is to reference
1966
1979
        :param force: skip the check for local commits in a heavy checkout
1967
1980
        :param revision_id: revision ID to switch to (or None)
1980
1993
            self.revision_id)
1981
1994
 
1982
1995
 
1983
 
class BranchFormatMetadir(BranchFormat):
1984
 
    """Common logic for meta-dir based branch formats."""
 
1996
class BranchFormatMetadir(bzrdir.BzrDirMetaComponentFormat, BranchFormat):
 
1997
    """Base class for branch formats that live in meta directories.
 
1998
    """
 
1999
 
 
2000
    def __init__(self):
 
2001
        BranchFormat.__init__(self)
 
2002
        bzrdir.BzrDirMetaComponentFormat.__init__(self)
 
2003
 
 
2004
    @classmethod
 
2005
    def find_format(klass, controldir, name=None):
 
2006
        """Return the format for the branch object in controldir."""
 
2007
        try:
 
2008
            transport = controldir.get_branch_transport(None, name=name)
 
2009
        except errors.NoSuchFile:
 
2010
            raise errors.NotBranchError(path=name, bzrdir=controldir)
 
2011
        try:
 
2012
            format_string = transport.get_bytes("format")
 
2013
        except errors.NoSuchFile:
 
2014
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
 
2015
        return klass._find_format(format_registry, 'branch', format_string)
1985
2016
 
1986
2017
    def _branch_class(self):
1987
2018
        """What class to instantiate on open calls."""
1988
2019
        raise NotImplementedError(self._branch_class)
1989
2020
 
 
2021
    def _get_initial_config(self, append_revisions_only=None):
 
2022
        if append_revisions_only:
 
2023
            return "append_revisions_only = True\n"
 
2024
        else:
 
2025
            # Avoid writing anything if append_revisions_only is disabled,
 
2026
            # as that is the default.
 
2027
            return ""
 
2028
 
1990
2029
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1991
2030
                           repository=None):
1992
2031
        """Initialize a branch in a bzrdir, with specified files
2016
2055
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2017
2056
        return branch
2018
2057
 
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
2058
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2027
 
            found_repository=None):
 
2059
            found_repository=None, possible_transports=None):
2028
2060
        """See BranchFormat.open()."""
2029
2061
        if not _found:
2030
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2062
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2031
2063
            if format.__class__ != self.__class__:
2032
2064
                raise AssertionError("wrong format %r found for %r" %
2033
2065
                    (format, self))
2042
2074
                              name=name,
2043
2075
                              a_bzrdir=a_bzrdir,
2044
2076
                              _repository=found_repository,
2045
 
                              ignore_fallbacks=ignore_fallbacks)
 
2077
                              ignore_fallbacks=ignore_fallbacks,
 
2078
                              possible_transports=possible_transports)
2046
2079
        except errors.NoSuchFile:
2047
2080
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2048
2081
 
2049
 
    def __init__(self):
2050
 
        super(BranchFormatMetadir, self).__init__()
2051
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2052
 
        self._matchingbzrdir.set_branch_format(self)
 
2082
    @property
 
2083
    def _matchingbzrdir(self):
 
2084
        ret = bzrdir.BzrDirMetaFormat1()
 
2085
        ret.set_branch_format(self)
 
2086
        return ret
2053
2087
 
2054
2088
    def supports_tags(self):
2055
2089
        return True
2074
2108
    def _branch_class(self):
2075
2109
        return BzrBranch5
2076
2110
 
2077
 
    def get_format_string(self):
 
2111
    @classmethod
 
2112
    def get_format_string(cls):
2078
2113
        """See BranchFormat.get_format_string()."""
2079
2114
        return "Bazaar-NG branch format 5\n"
2080
2115
 
2082
2117
        """See BranchFormat.get_format_description()."""
2083
2118
        return "Branch format 5"
2084
2119
 
2085
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2120
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2121
                   append_revisions_only=None):
2086
2122
        """Create a branch of this format in a_bzrdir."""
 
2123
        if append_revisions_only:
 
2124
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2087
2125
        utf8_files = [('revision-history', ''),
2088
2126
                      ('branch-name', ''),
2089
2127
                      ]
2107
2145
    def _branch_class(self):
2108
2146
        return BzrBranch6
2109
2147
 
2110
 
    def get_format_string(self):
 
2148
    @classmethod
 
2149
    def get_format_string(cls):
2111
2150
        """See BranchFormat.get_format_string()."""
2112
2151
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
2113
2152
 
2115
2154
        """See BranchFormat.get_format_description()."""
2116
2155
        return "Branch format 6"
2117
2156
 
2118
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2157
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2158
                   append_revisions_only=None):
2119
2159
        """Create a branch of this format in a_bzrdir."""
2120
2160
        utf8_files = [('last-revision', '0 null:\n'),
2121
 
                      ('branch.conf', ''),
 
2161
                      ('branch.conf',
 
2162
                          self._get_initial_config(append_revisions_only)),
2122
2163
                      ('tags', ''),
2123
2164
                      ]
2124
2165
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2125
2166
 
2126
2167
    def make_tags(self, branch):
2127
2168
        """See bzrlib.branch.BranchFormat.make_tags()."""
2128
 
        return BasicTags(branch)
 
2169
        return _mod_tag.BasicTags(branch)
2129
2170
 
2130
2171
    def supports_set_append_revisions_only(self):
2131
2172
        return True
2137
2178
    def _branch_class(self):
2138
2179
        return BzrBranch8
2139
2180
 
2140
 
    def get_format_string(self):
 
2181
    @classmethod
 
2182
    def get_format_string(cls):
2141
2183
        """See BranchFormat.get_format_string()."""
2142
2184
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2143
2185
 
2145
2187
        """See BranchFormat.get_format_description()."""
2146
2188
        return "Branch format 8"
2147
2189
 
2148
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2190
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2191
                   append_revisions_only=None):
2149
2192
        """Create a branch of this format in a_bzrdir."""
2150
2193
        utf8_files = [('last-revision', '0 null:\n'),
2151
 
                      ('branch.conf', ''),
 
2194
                      ('branch.conf',
 
2195
                          self._get_initial_config(append_revisions_only)),
2152
2196
                      ('tags', ''),
2153
2197
                      ('references', '')
2154
2198
                      ]
2156
2200
 
2157
2201
    def make_tags(self, branch):
2158
2202
        """See bzrlib.branch.BranchFormat.make_tags()."""
2159
 
        return BasicTags(branch)
 
2203
        return _mod_tag.BasicTags(branch)
2160
2204
 
2161
2205
    def supports_set_append_revisions_only(self):
2162
2206
        return True
2176
2220
    This format was introduced in bzr 1.6.
2177
2221
    """
2178
2222
 
2179
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2223
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2224
                   append_revisions_only=None):
2180
2225
        """Create a branch of this format in a_bzrdir."""
2181
2226
        utf8_files = [('last-revision', '0 null:\n'),
2182
 
                      ('branch.conf', ''),
 
2227
                      ('branch.conf',
 
2228
                          self._get_initial_config(append_revisions_only)),
2183
2229
                      ('tags', ''),
2184
2230
                      ]
2185
2231
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2187
2233
    def _branch_class(self):
2188
2234
        return BzrBranch7
2189
2235
 
2190
 
    def get_format_string(self):
 
2236
    @classmethod
 
2237
    def get_format_string(cls):
2191
2238
        """See BranchFormat.get_format_string()."""
2192
2239
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2193
2240
 
2203
2250
 
2204
2251
    def make_tags(self, branch):
2205
2252
        """See bzrlib.branch.BranchFormat.make_tags()."""
2206
 
        return BasicTags(branch)
 
2253
        return _mod_tag.BasicTags(branch)
2207
2254
 
2208
2255
    supports_reference_locations = False
2209
2256
 
2210
2257
 
2211
 
class BranchReferenceFormat(BranchFormat):
 
2258
class BranchReferenceFormat(BranchFormatMetadir):
2212
2259
    """Bzr branch reference format.
2213
2260
 
2214
2261
    Branch references are used in implementing checkouts, they
2219
2266
     - a format string
2220
2267
    """
2221
2268
 
2222
 
    def get_format_string(self):
 
2269
    @classmethod
 
2270
    def get_format_string(cls):
2223
2271
        """See BranchFormat.get_format_string()."""
2224
2272
        return "Bazaar-NG Branch Reference Format 1\n"
2225
2273
 
2238
2286
        location = transport.put_bytes('location', to_branch.base)
2239
2287
 
2240
2288
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2241
 
            repository=None):
 
2289
            repository=None, append_revisions_only=None):
2242
2290
        """Create a branch of this format in a_bzrdir."""
2243
2291
        if target_branch is None:
2244
2292
            # this format does not implement branch itself, thus the implicit
2245
2293
            # creation contract must see it as uninitializable
2246
2294
            raise errors.UninitializableFormat(self)
2247
2295
        mutter('creating branch reference in %s', a_bzrdir.user_url)
 
2296
        if a_bzrdir._format.fixed_components:
 
2297
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
2248
2298
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2249
2299
        branch_transport.put_bytes('location',
2250
 
            target_branch.bzrdir.user_url)
 
2300
            target_branch.user_url)
2251
2301
        branch_transport.put_bytes('format', self.get_format_string())
2252
2302
        branch = self.open(
2253
2303
            a_bzrdir, name, _found=True,
2255
2305
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2256
2306
        return branch
2257
2307
 
2258
 
    def __init__(self):
2259
 
        super(BranchReferenceFormat, self).__init__()
2260
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2261
 
        self._matchingbzrdir.set_branch_format(self)
2262
 
 
2263
2308
    def _make_reference_clone_function(format, a_branch):
2264
2309
        """Create a clone() routine for a branch dynamically."""
2265
2310
        def clone(to_bzrdir, revision_id=None,
2288
2333
        :param possible_transports: An optional reusable transports list.
2289
2334
        """
2290
2335
        if not _found:
2291
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2336
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2292
2337
            if format.__class__ != self.__class__:
2293
2338
                raise AssertionError("wrong format %r found for %r" %
2294
2339
                    (format, self))
2295
2340
        if location is None:
2296
2341
            location = self.get_reference(a_bzrdir, name)
2297
 
        real_bzrdir = bzrdir.BzrDir.open(
 
2342
        real_bzrdir = controldir.ControlDir.open(
2298
2343
            location, possible_transports=possible_transports)
2299
2344
        result = real_bzrdir.open_branch(name=name, 
2300
 
            ignore_fallbacks=ignore_fallbacks)
 
2345
            ignore_fallbacks=ignore_fallbacks,
 
2346
            possible_transports=possible_transports)
2301
2347
        # this changes the behaviour of result.clone to create a new reference
2302
2348
        # rather than a copy of the content of the branch.
2303
2349
        # I did not use a proxy object because that needs much more extensive
2384
2430
 
2385
2431
    def __init__(self, _format=None,
2386
2432
                 _control_files=None, a_bzrdir=None, name=None,
2387
 
                 _repository=None, ignore_fallbacks=False):
 
2433
                 _repository=None, ignore_fallbacks=False,
 
2434
                 possible_transports=None):
2388
2435
        """Create new branch object at a particular location."""
2389
2436
        if a_bzrdir is None:
2390
2437
            raise ValueError('a_bzrdir must be supplied')
2391
2438
        else:
2392
2439
            self.bzrdir = a_bzrdir
2393
 
        self._base = self.bzrdir.transport.clone('..').base
 
2440
        self._user_transport = self.bzrdir.transport.clone('..')
 
2441
        if name is not None:
 
2442
            self._user_transport.set_segment_parameter(
 
2443
                "branch", urlutils.escape(name))
 
2444
        self._base = self._user_transport.base
2394
2445
        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
2446
        self._format = _format
2399
2447
        if _control_files is None:
2400
2448
            raise ValueError('BzrBranch _control_files is None')
2401
2449
        self.control_files = _control_files
2402
2450
        self._transport = _control_files._transport
2403
2451
        self.repository = _repository
2404
 
        Branch.__init__(self)
 
2452
        Branch.__init__(self, possible_transports)
2405
2453
 
2406
2454
    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)
 
2455
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
2412
2456
 
2413
2457
    __repr__ = __str__
2414
2458
 
2418
2462
 
2419
2463
    base = property(_get_base, doc="The URL for the root of this branch.")
2420
2464
 
 
2465
    @property
 
2466
    def user_transport(self):
 
2467
        return self._user_transport
 
2468
 
2421
2469
    def _get_config(self):
2422
 
        return TransportConfig(self._transport, 'branch.conf')
 
2470
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
 
2471
 
 
2472
    def _get_config_store(self):
 
2473
        return _mod_config.BranchStore(self)
2423
2474
 
2424
2475
    def is_locked(self):
2425
2476
        return self.control_files.is_locked()
2506
2557
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2507
2558
        revision_id = _mod_revision.ensure_null(revision_id)
2508
2559
        old_revno, old_revid = self.last_revision_info()
2509
 
        if self._get_append_revisions_only():
 
2560
        if self.get_append_revisions_only():
2510
2561
            self._check_history_violation(revision_id)
2511
2562
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2512
2563
        self._write_last_revision_info(revno, revision_id)
2673
2724
        self._set_revision_history(history)
2674
2725
 
2675
2726
    def _read_last_revision_info(self):
2676
 
        rh = self.revision_history()
 
2727
        rh = self._revision_history()
2677
2728
        revno = len(rh)
2678
2729
        if revno:
2679
2730
            return (revno, rh[-1])
2733
2784
        if revision_id == _mod_revision.NULL_REVISION:
2734
2785
            new_history = []
2735
2786
        else:
2736
 
            new_history = self.revision_history()
 
2787
            new_history = self._revision_history()
2737
2788
        if revision_id is not None and new_history != []:
2738
2789
            try:
2739
2790
                new_history = new_history[:new_history.index(revision_id) + 1]
2767
2818
class BzrBranch8(BzrBranch):
2768
2819
    """A branch that stores tree-reference locations."""
2769
2820
 
2770
 
    def _open_hook(self):
 
2821
    def _open_hook(self, possible_transports=None):
2771
2822
        if self._ignore_fallbacks:
2772
2823
            return
 
2824
        if possible_transports is None:
 
2825
            possible_transports = [self.bzrdir.root_transport]
2773
2826
        try:
2774
2827
            url = self.get_stacked_on_url()
2775
2828
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2783
2836
                    raise AssertionError(
2784
2837
                        "'transform_fallback_location' hook %s returned "
2785
2838
                        "None, not a URL." % hook_name)
2786
 
            self._activate_fallback_location(url)
 
2839
            self._activate_fallback_location(url,
 
2840
                possible_transports=possible_transports)
2787
2841
 
2788
2842
    def __init__(self, *args, **kwargs):
2789
2843
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2951
3005
            raise errors.NotStacked(self)
2952
3006
        return stacked_url
2953
3007
 
2954
 
    def _get_append_revisions_only(self):
2955
 
        return self.get_config(
2956
 
            ).get_user_option_as_bool('append_revisions_only')
2957
 
 
2958
3008
    @needs_read_lock
2959
3009
    def get_rev_id(self, revno, history=None):
2960
3010
        """Find the revision id of the specified revno."""
2989
3039
            except errors.RevisionNotPresent, e:
2990
3040
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
2991
3041
            index = len(self._partial_revision_history_cache) - 1
 
3042
            if index < 0:
 
3043
                raise errors.NoSuchRevision(self, revision_id)
2992
3044
            if self._partial_revision_history_cache[index] != revision_id:
2993
3045
                raise errors.NoSuchRevision(self, revision_id)
2994
3046
        return self.revno() - index
3046
3098
    :ivar local_branch: target branch if there is a Master, else None
3047
3099
    :ivar target_branch: Target/destination branch object. (write locked)
3048
3100
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
 
3101
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3049
3102
    """
3050
3103
 
3051
3104
    @deprecated_method(deprecated_in((2, 3, 0)))
3057
3110
        return self.new_revno - self.old_revno
3058
3111
 
3059
3112
    def report(self, to_file):
 
3113
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
3114
        tag_updates = getattr(self, "tag_updates", None)
3060
3115
        if not is_quiet():
3061
 
            if self.old_revid == self.new_revid:
3062
 
                to_file.write('No revisions to pull.\n')
3063
 
            else:
 
3116
            if self.old_revid != self.new_revid:
3064
3117
                to_file.write('Now on revision %d.\n' % self.new_revno)
 
3118
            if tag_updates:
 
3119
                to_file.write('%d tag(s) updated.\n' % len(tag_updates))
 
3120
            if self.old_revid == self.new_revid and not tag_updates:
 
3121
                if not tag_conflicts:
 
3122
                    to_file.write('No revisions or tags to pull.\n')
 
3123
                else:
 
3124
                    to_file.write('No revisions to pull.\n')
3065
3125
        self._show_tag_conficts(to_file)
3066
3126
 
3067
3127
 
3093
3153
        return self.new_revno - self.old_revno
3094
3154
 
3095
3155
    def report(self, to_file):
3096
 
        """Write a human-readable description of the result."""
3097
 
        if self.old_revid == self.new_revid:
3098
 
            note('No new revisions to push.')
3099
 
        else:
3100
 
            note('Pushed up to revision %d.' % self.new_revno)
 
3156
        # TODO: This function gets passed a to_file, but then
 
3157
        # ignores it and calls note() instead. This is also
 
3158
        # inconsistent with PullResult(), which writes to stdout.
 
3159
        # -- JRV20110901, bug #838853
 
3160
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
3161
        tag_updates = getattr(self, "tag_updates", None)
 
3162
        if not is_quiet():
 
3163
            if self.old_revid != self.new_revid:
 
3164
                note(gettext('Pushed up to revision %d.') % self.new_revno)
 
3165
            if tag_updates:
 
3166
                note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
 
3167
            if self.old_revid == self.new_revid and not tag_updates:
 
3168
                if not tag_conflicts:
 
3169
                    note(gettext('No new revisions or tags to push.'))
 
3170
                else:
 
3171
                    note(gettext('No new revisions to push.'))
3101
3172
        self._show_tag_conficts(to_file)
3102
3173
 
3103
3174
 
3117
3188
        :param verbose: Requests more detailed display of what was checked,
3118
3189
            if any.
3119
3190
        """
3120
 
        note('checked branch %s format %s', self.branch.user_url,
3121
 
            self.branch._format)
 
3191
        note(gettext('checked branch {0} format {1}').format(
 
3192
                                self.branch.user_url, self.branch._format))
3122
3193
        for error in self.errors:
3123
 
            note('found error:%s', error)
 
3194
            note(gettext('found error:%s'), error)
3124
3195
 
3125
3196
 
3126
3197
class Converter5to6(object):
3165
3236
 
3166
3237
 
3167
3238
class Converter7to8(object):
3168
 
    """Perform an in-place upgrade of format 6 to format 7"""
 
3239
    """Perform an in-place upgrade of format 7 to format 8"""
3169
3240
 
3170
3241
    def convert(self, branch):
3171
3242
        format = BzrBranchFormat8()
3344
3415
        if local and not bound_location:
3345
3416
            raise errors.LocalRequiresBoundBranch()
3346
3417
        master_branch = None
3347
 
        source_is_master = (self.source.user_url == bound_location)
 
3418
        source_is_master = False
 
3419
        if bound_location:
 
3420
            # bound_location comes from a config file, some care has to be
 
3421
            # taken to relate it to source.user_url
 
3422
            normalized = urlutils.normalize_url(bound_location)
 
3423
            try:
 
3424
                relpath = self.source.user_transport.relpath(normalized)
 
3425
                source_is_master = (relpath == '')
 
3426
            except (errors.PathNotChild, errors.InvalidURL):
 
3427
                source_is_master = False
3348
3428
        if not local and bound_location and not source_is_master:
3349
3429
            # not pulling from master, so we need to update master.
3350
3430
            master_branch = self.target.get_master_branch(possible_transports)
3402
3482
            self._update_revisions(stop_revision, overwrite=overwrite,
3403
3483
                    graph=graph)
3404
3484
        if self.source._push_should_merge_tags():
3405
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3406
 
                overwrite)
 
3485
            result.tag_updates, result.tag_conflicts = (
 
3486
                self.source.tags.merge_to(self.target.tags, overwrite))
3407
3487
        result.new_revno, result.new_revid = self.target.last_revision_info()
3408
3488
        return result
3409
3489
 
3492
3572
            # TODO: The old revid should be specified when merging tags, 
3493
3573
            # so a tags implementation that versions tags can only 
3494
3574
            # pull in the most recent changes. -- JRV20090506
3495
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3496
 
                overwrite, ignore_master=not merge_tags_to_master)
 
3575
            result.tag_updates, result.tag_conflicts = (
 
3576
                self.source.tags.merge_to(self.target.tags, overwrite,
 
3577
                    ignore_master=not merge_tags_to_master))
3497
3578
            result.new_revno, result.new_revid = self.target.last_revision_info()
3498
3579
            if _hook_master:
3499
3580
                result.master_branch = _hook_master