~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

(gz) Fix deprecations of win32utils path function unicode wrappers (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 (
51
56
    controldir,
52
57
    )
66
71
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
67
72
 
68
73
 
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
74
class Branch(controldir.ControlComponent):
75
75
    """Branch holding a history of revisions.
76
76
 
93
93
    def user_transport(self):
94
94
        return self.bzrdir.user_transport
95
95
 
96
 
    def __init__(self, *ignored, **ignored_too):
 
96
    def __init__(self, possible_transports=None):
97
97
        self.tags = self._format.make_tags(self)
98
98
        self._revision_history_cache = None
99
99
        self._revision_id_to_revno_cache = None
103
103
        self._last_revision_info_cache = None
104
104
        self._master_branch_cache = None
105
105
        self._merge_sorted_revisions_cache = None
106
 
        self._open_hook()
 
106
        self._open_hook(possible_transports)
107
107
        hooks = Branch.hooks['open']
108
108
        for hook in hooks:
109
109
            hook(self)
110
110
 
111
 
    def _open_hook(self):
 
111
    def _open_hook(self, possible_transports):
112
112
        """Called by init to allow simpler extension of the base class."""
113
113
 
114
 
    def _activate_fallback_location(self, url):
 
114
    def _activate_fallback_location(self, url, possible_transports):
115
115
        """Activate the branch/repository from url as a fallback repository."""
116
116
        for existing_fallback_repo in self.repository._fallback_repositories:
117
117
            if existing_fallback_repo.user_url == url:
118
118
                # This fallback is already configured.  This probably only
119
 
                # happens because BzrDir.sprout is a horrible mess.  To avoid
 
119
                # happens because ControlDir.sprout is a horrible mess.  To avoid
120
120
                # confusing _unstack we don't add this a second time.
121
121
                mutter('duplicate activation of fallback %r on %r', url, self)
122
122
                return
123
 
        repo = self._get_fallback_repository(url)
 
123
        repo = self._get_fallback_repository(url, possible_transports)
124
124
        if repo.has_same_location(self.repository):
125
125
            raise errors.UnstackableLocationError(self.user_url, url)
126
126
        self.repository.add_fallback_repository(repo)
180
180
        For instance, if the branch is at URL/.bzr/branch,
181
181
        Branch.open(URL) -> a Branch instance.
182
182
        """
183
 
        control = bzrdir.BzrDir.open(base, _unsupported,
 
183
        control = controldir.ControlDir.open(base, _unsupported,
184
184
                                     possible_transports=possible_transports)
185
 
        return control.open_branch(unsupported=_unsupported)
 
185
        return control.open_branch(unsupported=_unsupported,
 
186
            possible_transports=possible_transports)
186
187
 
187
188
    @staticmethod
188
 
    def open_from_transport(transport, name=None, _unsupported=False):
 
189
    def open_from_transport(transport, name=None, _unsupported=False,
 
190
            possible_transports=None):
189
191
        """Open the branch rooted at transport"""
190
 
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
191
 
        return control.open_branch(name=name, unsupported=_unsupported)
 
192
        control = controldir.ControlDir.open_from_transport(transport, _unsupported)
 
193
        return control.open_branch(name=name, unsupported=_unsupported,
 
194
            possible_transports=possible_transports)
192
195
 
193
196
    @staticmethod
194
197
    def open_containing(url, possible_transports=None):
202
205
        format, UnknownFormatError or UnsupportedFormatError are raised.
203
206
        If there is one, it is returned, along with the unused portion of url.
204
207
        """
205
 
        control, relpath = bzrdir.BzrDir.open_containing(url,
 
208
        control, relpath = controldir.ControlDir.open_containing(url,
206
209
                                                         possible_transports)
207
 
        return control.open_branch(), relpath
 
210
        branch = control.open_branch(possible_transports=possible_transports)
 
211
        return (branch, relpath)
208
212
 
209
213
    def _push_should_merge_tags(self):
210
214
        """Should _basic_push merge this branch's tags into the target?
222
226
 
223
227
        :return: A bzrlib.config.BranchConfig.
224
228
        """
225
 
        return BranchConfig(self)
 
229
        return _mod_config.BranchConfig(self)
 
230
 
 
231
    def get_config_stack(self):
 
232
        """Get a bzrlib.config.BranchStack for this Branch.
 
233
 
 
234
        This can then be used to get and set configuration options for the
 
235
        branch.
 
236
 
 
237
        :return: A bzrlib.config.BranchStack.
 
238
        """
 
239
        return _mod_config.BranchStack(self)
226
240
 
227
241
    def _get_config(self):
228
242
        """Get the concrete config for just the config in this branch.
236
250
        """
237
251
        raise NotImplementedError(self._get_config)
238
252
 
239
 
    def _get_fallback_repository(self, url):
 
253
    def _get_fallback_repository(self, url, possible_transports):
240
254
        """Get the repository we fallback to at url."""
241
255
        url = urlutils.join(self.base, url)
242
 
        a_branch = Branch.open(url,
243
 
            possible_transports=[self.bzrdir.root_transport])
 
256
        a_branch = Branch.open(url, possible_transports=possible_transports)
244
257
        return a_branch.repository
245
258
 
246
259
    @needs_read_lock
520
533
                    # The decision to include the start or not
521
534
                    # depends on the stop_rule if a stop is provided
522
535
                    # so pop this node back into the iterator
523
 
                    rev_iter = chain(iter([node]), rev_iter)
 
536
                    rev_iter = itertools.chain(iter([node]), rev_iter)
524
537
                    break
525
538
        if stop_revision_id is None:
526
539
            # Yield everything
651
664
        """
652
665
        raise errors.UpgradeRequired(self.user_url)
653
666
 
 
667
    def get_append_revisions_only(self):
 
668
        """Whether it is only possible to append revisions to the history.
 
669
        """
 
670
        if not self._format.supports_set_append_revisions_only():
 
671
            return False
 
672
        return self.get_config_stack().get('append_revisions_only')
 
673
 
654
674
    def set_append_revisions_only(self, enabled):
655
675
        if not self._format.supports_set_append_revisions_only():
656
676
            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)
 
677
        self.get_config_stack().set('append_revisions_only', enabled)
663
678
 
664
679
    def set_reference_info(self, file_id, tree_path, branch_location):
665
680
        """Set the branch location to use for a tree reference."""
694
709
        """
695
710
        raise errors.UpgradeRequired(self.user_url)
696
711
 
697
 
    def get_commit_builder(self, parents, config=None, timestamp=None,
 
712
    def get_commit_builder(self, parents, config_stack=None, timestamp=None,
698
713
                           timezone=None, committer=None, revprops=None,
699
714
                           revision_id=None, lossy=False):
700
715
        """Obtain a CommitBuilder for this branch.
710
725
            represented, when pushing to a foreign VCS 
711
726
        """
712
727
 
713
 
        if config is None:
714
 
            config = self.get_config()
 
728
        if config_stack is None:
 
729
            config_stack = self.get_config_stack()
715
730
 
716
 
        return self.repository.get_commit_builder(self, parents, config,
 
731
        return self.repository.get_commit_builder(self, parents, config_stack,
717
732
            timestamp, timezone, committer, revprops, revision_id,
718
733
            lossy)
719
734
 
724
739
        """
725
740
        return None
726
741
 
 
742
    @deprecated_method(deprecated_in((2, 5, 0)))
727
743
    def get_revision_delta(self, revno):
728
744
        """Return the delta for one revision.
729
745
 
730
746
        The delta is relative to its mainline predecessor, or the
731
747
        empty tree for revision 1.
732
748
        """
733
 
        rh = self.revision_history()
734
 
        if not (1 <= revno <= len(rh)):
 
749
        try:
 
750
            revid = self.get_rev_id(revno)
 
751
        except errors.NoSuchRevision:
735
752
            raise errors.InvalidRevisionNumber(revno)
736
 
        return self.repository.get_revision_delta(rh[revno-1])
 
753
        return self.repository.get_revision_delta(revid)
737
754
 
738
755
    def get_stacked_on_url(self):
739
756
        """Get the URL this branch is stacked against.
785
802
                                  other_branch=None):
786
803
        """See Branch.generate_revision_history"""
787
804
        graph = self.repository.get_graph()
 
805
        (last_revno, last_revid) = self.last_revision_info()
788
806
        known_revision_ids = [
789
 
            self.last_revision_info(),
 
807
            (last_revid, last_revno),
790
808
            (_mod_revision.NULL_REVISION, 0),
791
809
            ]
792
810
        if last_rev is not None:
837
855
                return
838
856
            self._unstack()
839
857
        else:
840
 
            self._activate_fallback_location(url)
 
858
            self._activate_fallback_location(url,
 
859
                possible_transports=[self.bzrdir.root_transport])
841
860
        # write this out after the repository is stacked to avoid setting a
842
861
        # stacked config that doesn't work.
843
862
        self._set_config_location('stacked_on_location', url)
849
868
        """
850
869
        pb = ui.ui_factory.nested_progress_bar()
851
870
        try:
852
 
            pb.update("Unstacking")
 
871
            pb.update(gettext("Unstacking"))
853
872
            # The basic approach here is to fetch the tip of the branch,
854
873
            # including all available ghosts, from the existing stacked
855
874
            # repository into a new repository object without the fallbacks. 
869
888
            # stream from one of them to the other.  This does mean doing
870
889
            # separate SSH connection setup, but unstacking is not a
871
890
            # common operation so it's tolerable.
872
 
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
 
891
            new_bzrdir = controldir.ControlDir.open(
 
892
                self.bzrdir.root_transport.base)
873
893
            new_repository = new_bzrdir.find_repository()
874
894
            if new_repository._fallback_repositories:
875
895
                raise AssertionError("didn't expect %r to have "
918
938
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
919
939
                except errors.TagsNotSupported:
920
940
                    tags_to_fetch = set()
921
 
                fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
 
941
                fetch_spec = vf_search.NotInOtherForRevs(self.repository,
922
942
                    old_repository, required_ids=[self.last_revision()],
923
943
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
924
944
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
992
1012
        """
993
1013
        raise NotImplementedError(self._gen_revision_history)
994
1014
 
 
1015
    @deprecated_method(deprecated_in((2, 5, 0)))
995
1016
    @needs_read_lock
996
1017
    def revision_history(self):
997
1018
        """Return sequence of revision ids on this branch.
999
1020
        This method will cache the revision history for as long as it is safe to
1000
1021
        do so.
1001
1022
        """
 
1023
        return self._revision_history()
 
1024
 
 
1025
    def _revision_history(self):
1002
1026
        if 'evil' in debug.debug_flags:
1003
1027
            mutter_callsite(3, "revision_history scales with history.")
1004
1028
        if self._revision_history_cache is not None:
1074
1098
        """Given a revision id, return its revno"""
1075
1099
        if _mod_revision.is_null(revision_id):
1076
1100
            return 0
1077
 
        history = self.revision_history()
 
1101
        history = self._revision_history()
1078
1102
        try:
1079
1103
            return history.index(revision_id) + 1
1080
1104
        except ValueError:
1293
1317
            if repository_policy is not None:
1294
1318
                repository_policy.configure_branch(result)
1295
1319
            self.copy_content_into(result, revision_id=revision_id)
1296
 
            master_branch = self.get_master_branch()
1297
 
            if master_branch is None:
 
1320
            master_url = self.get_bound_location()
 
1321
            if master_url is None:
1298
1322
                result.set_parent(self.bzrdir.root_transport.base)
1299
1323
            else:
1300
 
                result.set_parent(master_branch.bzrdir.root_transport.base)
 
1324
                result.set_parent(master_url)
1301
1325
        finally:
1302
1326
            result.unlock()
1303
1327
        return result
1377
1401
        # TODO: We should probably also check that self.revision_history
1378
1402
        # matches the repository for older branch formats.
1379
1403
        # If looking for the code that cross-checks repository parents against
1380
 
        # the iter_reverse_revision_history output, that is now a repository
 
1404
        # the Graph.iter_lefthand_ancestry output, that is now a repository
1381
1405
        # specific check.
1382
1406
        return result
1383
1407
 
1384
 
    def _get_checkout_format(self):
 
1408
    def _get_checkout_format(self, lightweight=False):
1385
1409
        """Return the most suitable metadir for a checkout of this branch.
1386
1410
        Weaves are used if this branch's repository uses weaves.
1387
1411
        """
1433
1457
        """
1434
1458
        t = transport.get_transport(to_location)
1435
1459
        t.ensure_base()
 
1460
        format = self._get_checkout_format(lightweight=lightweight)
1436
1461
        if lightweight:
1437
 
            format = self._get_checkout_format()
1438
1462
            checkout = format.initialize_on_transport(t)
1439
1463
            from_branch = BranchReferenceFormat().initialize(checkout, 
1440
1464
                target_branch=self)
1441
1465
        else:
1442
 
            format = self._get_checkout_format()
1443
 
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
 
1466
            checkout_branch = controldir.ControlDir.create_branch_convenience(
1444
1467
                to_location, force_new_tree=False, format=format)
1445
1468
            checkout = checkout_branch.bzrdir
1446
1469
            checkout_branch.bind(self)
1545
1568
        # For bzr native formats must_fetch is just the tip, and if_present_fetch
1546
1569
        # are the tags.
1547
1570
        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()
 
1571
        if_present_fetch = set()
 
1572
        c = self.get_config()
 
1573
        include_tags = c.get_user_option_as_bool('branch.fetch_tags',
 
1574
                                                 default=False)
 
1575
        if include_tags:
 
1576
            try:
 
1577
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
 
1578
            except errors.TagsNotSupported:
 
1579
                pass
1552
1580
        must_fetch.discard(_mod_revision.NULL_REVISION)
1553
1581
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
1554
1582
        return must_fetch, if_present_fetch
1572
1600
    object will be created every time regardless.
1573
1601
    """
1574
1602
 
1575
 
    can_set_append_revisions_only = True
1576
 
 
1577
1603
    def __eq__(self, other):
1578
1604
        return self.__class__ is other.__class__
1579
1605
 
1581
1607
        return not (self == other)
1582
1608
 
1583
1609
    @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
1610
    @deprecated_method(deprecated_in((2, 4, 0)))
1597
1611
    def get_default_format(klass):
1598
1612
        """Return the current default format."""
1608
1622
        """
1609
1623
        return format_registry._get_all()
1610
1624
 
1611
 
    def get_reference(self, a_bzrdir, name=None):
1612
 
        """Get the target reference of the branch in a_bzrdir.
 
1625
    def get_reference(self, controldir, name=None):
 
1626
        """Get the target reference of the branch in controldir.
1613
1627
 
1614
1628
        format probing must have been completed before calling
1615
1629
        this method - it is assumed that the format of the branch
1616
 
        in a_bzrdir is correct.
 
1630
        in controldir is correct.
1617
1631
 
1618
 
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1632
        :param controldir: The controldir to get the branch data from.
1619
1633
        :param name: Name of the colocated branch to fetch
1620
1634
        :return: None if the branch is not a reference branch.
1621
1635
        """
1622
1636
        return None
1623
1637
 
1624
1638
    @classmethod
1625
 
    def set_reference(self, a_bzrdir, name, to_branch):
1626
 
        """Set the target reference of the branch in a_bzrdir.
 
1639
    def set_reference(self, controldir, name, to_branch):
 
1640
        """Set the target reference of the branch in controldir.
1627
1641
 
1628
1642
        format probing must have been completed before calling
1629
1643
        this method - it is assumed that the format of the branch
1630
 
        in a_bzrdir is correct.
 
1644
        in controldir is correct.
1631
1645
 
1632
 
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1646
        :param controldir: The controldir to set the branch reference for.
1633
1647
        :param name: Name of colocated branch to set, None for default
1634
1648
        :param to_branch: branch that the checkout is to reference
1635
1649
        """
1636
1650
        raise NotImplementedError(self.set_reference)
1637
1651
 
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
1652
    def get_format_description(self):
1643
1653
        """Return the short format description for this format."""
1644
1654
        raise NotImplementedError(self.get_format_description)
1645
1655
 
1646
 
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
 
1656
    def _run_post_branch_init_hooks(self, controldir, name, branch):
1647
1657
        hooks = Branch.hooks['post_branch_init']
1648
1658
        if not hooks:
1649
1659
            return
1650
 
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
 
1660
        params = BranchInitHookParams(self, controldir, name, branch)
1651
1661
        for hook in hooks:
1652
1662
            hook(params)
1653
1663
 
1654
 
    def initialize(self, a_bzrdir, name=None, repository=None):
1655
 
        """Create a branch of this format in a_bzrdir.
1656
 
        
 
1664
    def initialize(self, controldir, name=None, repository=None,
 
1665
                   append_revisions_only=None):
 
1666
        """Create a branch of this format in controldir.
 
1667
 
1657
1668
        :param name: Name of the colocated branch to create.
1658
1669
        """
1659
1670
        raise NotImplementedError(self.initialize)
1679
1690
        Note that it is normal for branch to be a RemoteBranch when using tags
1680
1691
        on a RemoteBranch.
1681
1692
        """
1682
 
        return DisabledTags(branch)
 
1693
        return _mod_tag.DisabledTags(branch)
1683
1694
 
1684
1695
    def network_name(self):
1685
1696
        """A simple byte string uniquely identifying this format for RPC calls.
1691
1702
        """
1692
1703
        raise NotImplementedError(self.network_name)
1693
1704
 
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
 
1705
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
 
1706
            found_repository=None, possible_transports=None):
 
1707
        """Return the branch object for controldir.
1697
1708
 
1698
 
        :param a_bzrdir: A BzrDir that contains a branch.
 
1709
        :param controldir: A ControlDir that contains a branch.
1699
1710
        :param name: Name of colocated branch to open
1700
1711
        :param _found: a private parameter, do not use it. It is used to
1701
1712
            indicate if format probing has already be done.
1743
1754
        """True if this format supports tags stored in the branch"""
1744
1755
        return False  # by default
1745
1756
 
 
1757
    def tags_are_versioned(self):
 
1758
        """Whether the tag container for this branch versions tags."""
 
1759
        return False
 
1760
 
 
1761
    def supports_tags_referencing_ghosts(self):
 
1762
        """True if tags can reference ghost revisions."""
 
1763
        return True
 
1764
 
1746
1765
 
1747
1766
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1748
1767
    """A factory for a BranchFormat object, permitting simple lazy registration.
1762
1781
        """
1763
1782
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
1764
1783
        self._format_string = format_string
1765
 
        
 
1784
 
1766
1785
    def get_format_string(self):
1767
1786
        """See BranchFormat.get_format_string."""
1768
1787
        return self._format_string
1913
1932
    There are 4 fields that hooks may wish to access:
1914
1933
 
1915
1934
    :ivar format: the branch format
1916
 
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
 
1935
    :ivar bzrdir: the ControlDir where the branch will be/has been initialized
1917
1936
    :ivar name: name of colocated branch, if any (or None)
1918
1937
    :ivar branch: the branch created
1919
1938
 
1922
1941
    branch, which refer to the original branch.
1923
1942
    """
1924
1943
 
1925
 
    def __init__(self, format, a_bzrdir, name, branch):
 
1944
    def __init__(self, format, controldir, name, branch):
1926
1945
        """Create a group of BranchInitHook parameters.
1927
1946
 
1928
1947
        :param format: the branch format
1929
 
        :param a_bzrdir: the BzrDir where the branch will be/has been
 
1948
        :param controldir: the ControlDir where the branch will be/has been
1930
1949
            initialized
1931
1950
        :param name: name of colocated branch, if any (or None)
1932
1951
        :param branch: the branch created
1936
1955
        in branch, which refer to the original branch.
1937
1956
        """
1938
1957
        self.format = format
1939
 
        self.bzrdir = a_bzrdir
 
1958
        self.bzrdir = controldir
1940
1959
        self.name = name
1941
1960
        self.branch = branch
1942
1961
 
1952
1971
 
1953
1972
    There are 4 fields that hooks may wish to access:
1954
1973
 
1955
 
    :ivar control_dir: BzrDir of the checkout to change
 
1974
    :ivar control_dir: ControlDir of the checkout to change
1956
1975
    :ivar to_branch: branch that the checkout is to reference
1957
1976
    :ivar force: skip the check for local commits in a heavy checkout
1958
1977
    :ivar revision_id: revision ID to switch to (or None)
1961
1980
    def __init__(self, control_dir, to_branch, force, revision_id):
1962
1981
        """Create a group of SwitchHook parameters.
1963
1982
 
1964
 
        :param control_dir: BzrDir of the checkout to change
 
1983
        :param control_dir: ControlDir of the checkout to change
1965
1984
        :param to_branch: branch that the checkout is to reference
1966
1985
        :param force: skip the check for local commits in a heavy checkout
1967
1986
        :param revision_id: revision ID to switch to (or None)
1980
1999
            self.revision_id)
1981
2000
 
1982
2001
 
1983
 
class BranchFormatMetadir(BranchFormat):
1984
 
    """Common logic for meta-dir based branch formats."""
 
2002
class BranchFormatMetadir(bzrdir.BzrDirMetaComponentFormat, BranchFormat):
 
2003
    """Base class for branch formats that live in meta directories.
 
2004
    """
 
2005
 
 
2006
    def __init__(self):
 
2007
        BranchFormat.__init__(self)
 
2008
        bzrdir.BzrDirMetaComponentFormat.__init__(self)
 
2009
 
 
2010
    @classmethod
 
2011
    def find_format(klass, controldir, name=None):
 
2012
        """Return the format for the branch object in controldir."""
 
2013
        try:
 
2014
            transport = controldir.get_branch_transport(None, name=name)
 
2015
        except errors.NoSuchFile:
 
2016
            raise errors.NotBranchError(path=name, bzrdir=controldir)
 
2017
        try:
 
2018
            format_string = transport.get_bytes("format")
 
2019
        except errors.NoSuchFile:
 
2020
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
 
2021
        return klass._find_format(format_registry, 'branch', format_string)
1985
2022
 
1986
2023
    def _branch_class(self):
1987
2024
        """What class to instantiate on open calls."""
1988
2025
        raise NotImplementedError(self._branch_class)
1989
2026
 
 
2027
    def _get_initial_config(self, append_revisions_only=None):
 
2028
        if append_revisions_only:
 
2029
            return "append_revisions_only = True\n"
 
2030
        else:
 
2031
            # Avoid writing anything if append_revisions_only is disabled,
 
2032
            # as that is the default.
 
2033
            return ""
 
2034
 
1990
2035
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1991
2036
                           repository=None):
1992
2037
        """Initialize a branch in a bzrdir, with specified files
2016
2061
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2017
2062
        return branch
2018
2063
 
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
2064
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2027
 
            found_repository=None):
 
2065
            found_repository=None, possible_transports=None):
2028
2066
        """See BranchFormat.open()."""
2029
2067
        if not _found:
2030
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2068
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2031
2069
            if format.__class__ != self.__class__:
2032
2070
                raise AssertionError("wrong format %r found for %r" %
2033
2071
                    (format, self))
2042
2080
                              name=name,
2043
2081
                              a_bzrdir=a_bzrdir,
2044
2082
                              _repository=found_repository,
2045
 
                              ignore_fallbacks=ignore_fallbacks)
 
2083
                              ignore_fallbacks=ignore_fallbacks,
 
2084
                              possible_transports=possible_transports)
2046
2085
        except errors.NoSuchFile:
2047
2086
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2048
2087
 
2049
 
    def __init__(self):
2050
 
        super(BranchFormatMetadir, self).__init__()
2051
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2052
 
        self._matchingbzrdir.set_branch_format(self)
 
2088
    @property
 
2089
    def _matchingbzrdir(self):
 
2090
        ret = bzrdir.BzrDirMetaFormat1()
 
2091
        ret.set_branch_format(self)
 
2092
        return ret
2053
2093
 
2054
2094
    def supports_tags(self):
2055
2095
        return True
2074
2114
    def _branch_class(self):
2075
2115
        return BzrBranch5
2076
2116
 
2077
 
    def get_format_string(self):
 
2117
    @classmethod
 
2118
    def get_format_string(cls):
2078
2119
        """See BranchFormat.get_format_string()."""
2079
2120
        return "Bazaar-NG branch format 5\n"
2080
2121
 
2082
2123
        """See BranchFormat.get_format_description()."""
2083
2124
        return "Branch format 5"
2084
2125
 
2085
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2126
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2127
                   append_revisions_only=None):
2086
2128
        """Create a branch of this format in a_bzrdir."""
 
2129
        if append_revisions_only:
 
2130
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2087
2131
        utf8_files = [('revision-history', ''),
2088
2132
                      ('branch-name', ''),
2089
2133
                      ]
2107
2151
    def _branch_class(self):
2108
2152
        return BzrBranch6
2109
2153
 
2110
 
    def get_format_string(self):
 
2154
    @classmethod
 
2155
    def get_format_string(cls):
2111
2156
        """See BranchFormat.get_format_string()."""
2112
2157
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
2113
2158
 
2115
2160
        """See BranchFormat.get_format_description()."""
2116
2161
        return "Branch format 6"
2117
2162
 
2118
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2163
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2164
                   append_revisions_only=None):
2119
2165
        """Create a branch of this format in a_bzrdir."""
2120
2166
        utf8_files = [('last-revision', '0 null:\n'),
2121
 
                      ('branch.conf', ''),
 
2167
                      ('branch.conf',
 
2168
                          self._get_initial_config(append_revisions_only)),
2122
2169
                      ('tags', ''),
2123
2170
                      ]
2124
2171
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2125
2172
 
2126
2173
    def make_tags(self, branch):
2127
2174
        """See bzrlib.branch.BranchFormat.make_tags()."""
2128
 
        return BasicTags(branch)
 
2175
        return _mod_tag.BasicTags(branch)
2129
2176
 
2130
2177
    def supports_set_append_revisions_only(self):
2131
2178
        return True
2137
2184
    def _branch_class(self):
2138
2185
        return BzrBranch8
2139
2186
 
2140
 
    def get_format_string(self):
 
2187
    @classmethod
 
2188
    def get_format_string(cls):
2141
2189
        """See BranchFormat.get_format_string()."""
2142
2190
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2143
2191
 
2145
2193
        """See BranchFormat.get_format_description()."""
2146
2194
        return "Branch format 8"
2147
2195
 
2148
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2196
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2197
                   append_revisions_only=None):
2149
2198
        """Create a branch of this format in a_bzrdir."""
2150
2199
        utf8_files = [('last-revision', '0 null:\n'),
2151
 
                      ('branch.conf', ''),
 
2200
                      ('branch.conf',
 
2201
                          self._get_initial_config(append_revisions_only)),
2152
2202
                      ('tags', ''),
2153
2203
                      ('references', '')
2154
2204
                      ]
2156
2206
 
2157
2207
    def make_tags(self, branch):
2158
2208
        """See bzrlib.branch.BranchFormat.make_tags()."""
2159
 
        return BasicTags(branch)
 
2209
        return _mod_tag.BasicTags(branch)
2160
2210
 
2161
2211
    def supports_set_append_revisions_only(self):
2162
2212
        return True
2176
2226
    This format was introduced in bzr 1.6.
2177
2227
    """
2178
2228
 
2179
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2229
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2230
                   append_revisions_only=None):
2180
2231
        """Create a branch of this format in a_bzrdir."""
2181
2232
        utf8_files = [('last-revision', '0 null:\n'),
2182
 
                      ('branch.conf', ''),
 
2233
                      ('branch.conf',
 
2234
                          self._get_initial_config(append_revisions_only)),
2183
2235
                      ('tags', ''),
2184
2236
                      ]
2185
2237
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2187
2239
    def _branch_class(self):
2188
2240
        return BzrBranch7
2189
2241
 
2190
 
    def get_format_string(self):
 
2242
    @classmethod
 
2243
    def get_format_string(cls):
2191
2244
        """See BranchFormat.get_format_string()."""
2192
2245
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2193
2246
 
2203
2256
 
2204
2257
    def make_tags(self, branch):
2205
2258
        """See bzrlib.branch.BranchFormat.make_tags()."""
2206
 
        return BasicTags(branch)
 
2259
        return _mod_tag.BasicTags(branch)
2207
2260
 
2208
2261
    supports_reference_locations = False
2209
2262
 
2210
2263
 
2211
 
class BranchReferenceFormat(BranchFormat):
 
2264
class BranchReferenceFormat(BranchFormatMetadir):
2212
2265
    """Bzr branch reference format.
2213
2266
 
2214
2267
    Branch references are used in implementing checkouts, they
2219
2272
     - a format string
2220
2273
    """
2221
2274
 
2222
 
    def get_format_string(self):
 
2275
    @classmethod
 
2276
    def get_format_string(cls):
2223
2277
        """See BranchFormat.get_format_string()."""
2224
2278
        return "Bazaar-NG Branch Reference Format 1\n"
2225
2279
 
2238
2292
        location = transport.put_bytes('location', to_branch.base)
2239
2293
 
2240
2294
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2241
 
            repository=None):
 
2295
            repository=None, append_revisions_only=None):
2242
2296
        """Create a branch of this format in a_bzrdir."""
2243
2297
        if target_branch is None:
2244
2298
            # this format does not implement branch itself, thus the implicit
2245
2299
            # creation contract must see it as uninitializable
2246
2300
            raise errors.UninitializableFormat(self)
2247
2301
        mutter('creating branch reference in %s', a_bzrdir.user_url)
 
2302
        if a_bzrdir._format.fixed_components:
 
2303
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
2248
2304
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2249
2305
        branch_transport.put_bytes('location',
2250
 
            target_branch.bzrdir.user_url)
 
2306
            target_branch.user_url)
2251
2307
        branch_transport.put_bytes('format', self.get_format_string())
2252
2308
        branch = self.open(
2253
2309
            a_bzrdir, name, _found=True,
2255
2311
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2256
2312
        return branch
2257
2313
 
2258
 
    def __init__(self):
2259
 
        super(BranchReferenceFormat, self).__init__()
2260
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2261
 
        self._matchingbzrdir.set_branch_format(self)
2262
 
 
2263
2314
    def _make_reference_clone_function(format, a_branch):
2264
2315
        """Create a clone() routine for a branch dynamically."""
2265
2316
        def clone(to_bzrdir, revision_id=None,
2288
2339
        :param possible_transports: An optional reusable transports list.
2289
2340
        """
2290
2341
        if not _found:
2291
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2342
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2292
2343
            if format.__class__ != self.__class__:
2293
2344
                raise AssertionError("wrong format %r found for %r" %
2294
2345
                    (format, self))
2295
2346
        if location is None:
2296
2347
            location = self.get_reference(a_bzrdir, name)
2297
 
        real_bzrdir = bzrdir.BzrDir.open(
 
2348
        real_bzrdir = controldir.ControlDir.open(
2298
2349
            location, possible_transports=possible_transports)
2299
2350
        result = real_bzrdir.open_branch(name=name, 
2300
 
            ignore_fallbacks=ignore_fallbacks)
 
2351
            ignore_fallbacks=ignore_fallbacks,
 
2352
            possible_transports=possible_transports)
2301
2353
        # this changes the behaviour of result.clone to create a new reference
2302
2354
        # rather than a copy of the content of the branch.
2303
2355
        # I did not use a proxy object because that needs much more extensive
2384
2436
 
2385
2437
    def __init__(self, _format=None,
2386
2438
                 _control_files=None, a_bzrdir=None, name=None,
2387
 
                 _repository=None, ignore_fallbacks=False):
 
2439
                 _repository=None, ignore_fallbacks=False,
 
2440
                 possible_transports=None):
2388
2441
        """Create new branch object at a particular location."""
2389
2442
        if a_bzrdir is None:
2390
2443
            raise ValueError('a_bzrdir must be supplied')
2391
2444
        else:
2392
2445
            self.bzrdir = a_bzrdir
2393
 
        self._base = self.bzrdir.transport.clone('..').base
 
2446
        self._user_transport = self.bzrdir.transport.clone('..')
 
2447
        if name is not None:
 
2448
            self._user_transport.set_segment_parameter(
 
2449
                "branch", urlutils.escape(name))
 
2450
        self._base = self._user_transport.base
2394
2451
        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
2452
        self._format = _format
2399
2453
        if _control_files is None:
2400
2454
            raise ValueError('BzrBranch _control_files is None')
2401
2455
        self.control_files = _control_files
2402
2456
        self._transport = _control_files._transport
2403
2457
        self.repository = _repository
2404
 
        Branch.__init__(self)
 
2458
        Branch.__init__(self, possible_transports)
2405
2459
 
2406
2460
    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)
 
2461
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
2412
2462
 
2413
2463
    __repr__ = __str__
2414
2464
 
2418
2468
 
2419
2469
    base = property(_get_base, doc="The URL for the root of this branch.")
2420
2470
 
 
2471
    @property
 
2472
    def user_transport(self):
 
2473
        return self._user_transport
 
2474
 
2421
2475
    def _get_config(self):
2422
 
        return TransportConfig(self._transport, 'branch.conf')
 
2476
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
 
2477
 
 
2478
    def _get_config_store(self):
 
2479
        return _mod_config.BranchStore(self)
2423
2480
 
2424
2481
    def is_locked(self):
2425
2482
        return self.control_files.is_locked()
2506
2563
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2507
2564
        revision_id = _mod_revision.ensure_null(revision_id)
2508
2565
        old_revno, old_revid = self.last_revision_info()
2509
 
        if self._get_append_revisions_only():
 
2566
        if self.get_append_revisions_only():
2510
2567
            self._check_history_violation(revision_id)
2511
2568
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2512
2569
        self._write_last_revision_info(revno, revision_id)
2673
2730
        self._set_revision_history(history)
2674
2731
 
2675
2732
    def _read_last_revision_info(self):
2676
 
        rh = self.revision_history()
 
2733
        rh = self._revision_history()
2677
2734
        revno = len(rh)
2678
2735
        if revno:
2679
2736
            return (revno, rh[-1])
2733
2790
        if revision_id == _mod_revision.NULL_REVISION:
2734
2791
            new_history = []
2735
2792
        else:
2736
 
            new_history = self.revision_history()
 
2793
            new_history = self._revision_history()
2737
2794
        if revision_id is not None and new_history != []:
2738
2795
            try:
2739
2796
                new_history = new_history[:new_history.index(revision_id) + 1]
2767
2824
class BzrBranch8(BzrBranch):
2768
2825
    """A branch that stores tree-reference locations."""
2769
2826
 
2770
 
    def _open_hook(self):
 
2827
    def _open_hook(self, possible_transports=None):
2771
2828
        if self._ignore_fallbacks:
2772
2829
            return
 
2830
        if possible_transports is None:
 
2831
            possible_transports = [self.bzrdir.root_transport]
2773
2832
        try:
2774
2833
            url = self.get_stacked_on_url()
2775
2834
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2783
2842
                    raise AssertionError(
2784
2843
                        "'transform_fallback_location' hook %s returned "
2785
2844
                        "None, not a URL." % hook_name)
2786
 
            self._activate_fallback_location(url)
 
2845
            self._activate_fallback_location(url,
 
2846
                possible_transports=possible_transports)
2787
2847
 
2788
2848
    def __init__(self, *args, **kwargs):
2789
2849
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2951
3011
            raise errors.NotStacked(self)
2952
3012
        return stacked_url
2953
3013
 
2954
 
    def _get_append_revisions_only(self):
2955
 
        return self.get_config(
2956
 
            ).get_user_option_as_bool('append_revisions_only')
2957
 
 
2958
3014
    @needs_read_lock
2959
3015
    def get_rev_id(self, revno, history=None):
2960
3016
        """Find the revision id of the specified revno."""
2989
3045
            except errors.RevisionNotPresent, e:
2990
3046
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
2991
3047
            index = len(self._partial_revision_history_cache) - 1
 
3048
            if index < 0:
 
3049
                raise errors.NoSuchRevision(self, revision_id)
2992
3050
            if self._partial_revision_history_cache[index] != revision_id:
2993
3051
                raise errors.NoSuchRevision(self, revision_id)
2994
3052
        return self.revno() - index
3046
3104
    :ivar local_branch: target branch if there is a Master, else None
3047
3105
    :ivar target_branch: Target/destination branch object. (write locked)
3048
3106
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
 
3107
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3049
3108
    """
3050
3109
 
3051
3110
    @deprecated_method(deprecated_in((2, 3, 0)))
3057
3116
        return self.new_revno - self.old_revno
3058
3117
 
3059
3118
    def report(self, to_file):
 
3119
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
3120
        tag_updates = getattr(self, "tag_updates", None)
3060
3121
        if not is_quiet():
3061
 
            if self.old_revid == self.new_revid:
3062
 
                to_file.write('No revisions to pull.\n')
3063
 
            else:
 
3122
            if self.old_revid != self.new_revid:
3064
3123
                to_file.write('Now on revision %d.\n' % self.new_revno)
 
3124
            if tag_updates:
 
3125
                to_file.write('%d tag(s) updated.\n' % len(tag_updates))
 
3126
            if self.old_revid == self.new_revid and not tag_updates:
 
3127
                if not tag_conflicts:
 
3128
                    to_file.write('No revisions or tags to pull.\n')
 
3129
                else:
 
3130
                    to_file.write('No revisions to pull.\n')
3065
3131
        self._show_tag_conficts(to_file)
3066
3132
 
3067
3133
 
3093
3159
        return self.new_revno - self.old_revno
3094
3160
 
3095
3161
    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)
 
3162
        # TODO: This function gets passed a to_file, but then
 
3163
        # ignores it and calls note() instead. This is also
 
3164
        # inconsistent with PullResult(), which writes to stdout.
 
3165
        # -- JRV20110901, bug #838853
 
3166
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
3167
        tag_updates = getattr(self, "tag_updates", None)
 
3168
        if not is_quiet():
 
3169
            if self.old_revid != self.new_revid:
 
3170
                note(gettext('Pushed up to revision %d.') % self.new_revno)
 
3171
            if tag_updates:
 
3172
                note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
 
3173
            if self.old_revid == self.new_revid and not tag_updates:
 
3174
                if not tag_conflicts:
 
3175
                    note(gettext('No new revisions or tags to push.'))
 
3176
                else:
 
3177
                    note(gettext('No new revisions to push.'))
3101
3178
        self._show_tag_conficts(to_file)
3102
3179
 
3103
3180
 
3117
3194
        :param verbose: Requests more detailed display of what was checked,
3118
3195
            if any.
3119
3196
        """
3120
 
        note('checked branch %s format %s', self.branch.user_url,
3121
 
            self.branch._format)
 
3197
        note(gettext('checked branch {0} format {1}').format(
 
3198
                                self.branch.user_url, self.branch._format))
3122
3199
        for error in self.errors:
3123
 
            note('found error:%s', error)
 
3200
            note(gettext('found error:%s'), error)
3124
3201
 
3125
3202
 
3126
3203
class Converter5to6(object):
3165
3242
 
3166
3243
 
3167
3244
class Converter7to8(object):
3168
 
    """Perform an in-place upgrade of format 6 to format 7"""
 
3245
    """Perform an in-place upgrade of format 7 to format 8"""
3169
3246
 
3170
3247
    def convert(self, branch):
3171
3248
        format = BzrBranchFormat8()
3344
3421
        if local and not bound_location:
3345
3422
            raise errors.LocalRequiresBoundBranch()
3346
3423
        master_branch = None
3347
 
        source_is_master = (self.source.user_url == bound_location)
 
3424
        source_is_master = False
 
3425
        if bound_location:
 
3426
            # bound_location comes from a config file, some care has to be
 
3427
            # taken to relate it to source.user_url
 
3428
            normalized = urlutils.normalize_url(bound_location)
 
3429
            try:
 
3430
                relpath = self.source.user_transport.relpath(normalized)
 
3431
                source_is_master = (relpath == '')
 
3432
            except (errors.PathNotChild, errors.InvalidURL):
 
3433
                source_is_master = False
3348
3434
        if not local and bound_location and not source_is_master:
3349
3435
            # not pulling from master, so we need to update master.
3350
3436
            master_branch = self.target.get_master_branch(possible_transports)
3402
3488
            self._update_revisions(stop_revision, overwrite=overwrite,
3403
3489
                    graph=graph)
3404
3490
        if self.source._push_should_merge_tags():
3405
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3406
 
                overwrite)
 
3491
            result.tag_updates, result.tag_conflicts = (
 
3492
                self.source.tags.merge_to(self.target.tags, overwrite))
3407
3493
        result.new_revno, result.new_revid = self.target.last_revision_info()
3408
3494
        return result
3409
3495
 
3492
3578
            # TODO: The old revid should be specified when merging tags, 
3493
3579
            # so a tags implementation that versions tags can only 
3494
3580
            # 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)
 
3581
            result.tag_updates, result.tag_conflicts = (
 
3582
                self.source.tags.merge_to(self.target.tags, overwrite,
 
3583
                    ignore_master=not merge_tags_to_master))
3497
3584
            result.new_revno, result.new_revid = self.target.last_revision_info()
3498
3585
            if _hook_master:
3499
3586
                result.master_branch = _hook_master