~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Patch Queue Manager
  • Date: 2012-09-05 20:52:26 UTC
  • mfrom: (6549.3.1 dev_2.5_integration)
  • Revision ID: pqm@pqm.ubuntu.com-20120905205226-8s3bzolvduug3ifj
(gz) Merge 2.5 (Martin Packman)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2012 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
from __future__ import absolute_import
 
18
 
17
19
import bzrlib.bzrdir
18
20
 
19
21
from cStringIO import StringIO
37
39
    repository,
38
40
    revision as _mod_revision,
39
41
    rio,
 
42
    shelf,
40
43
    tag as _mod_tag,
41
44
    transport,
42
45
    ui,
43
46
    urlutils,
 
47
    vf_search,
44
48
    )
45
49
from bzrlib.i18n import gettext, ngettext
46
50
""")
47
51
 
 
52
# Explicitly import bzrlib.bzrdir so that the BzrProber
 
53
# is guaranteed to be registered.
 
54
import bzrlib.bzrdir
 
55
 
48
56
from bzrlib import (
 
57
    bzrdir,
49
58
    controldir,
50
59
    )
51
60
from bzrlib.decorators import (
86
95
    def user_transport(self):
87
96
        return self.bzrdir.user_transport
88
97
 
89
 
    def __init__(self, *ignored, **ignored_too):
 
98
    def __init__(self, possible_transports=None):
90
99
        self.tags = self._format.make_tags(self)
91
100
        self._revision_history_cache = None
92
101
        self._revision_id_to_revno_cache = None
96
105
        self._last_revision_info_cache = None
97
106
        self._master_branch_cache = None
98
107
        self._merge_sorted_revisions_cache = None
99
 
        self._open_hook()
 
108
        self._open_hook(possible_transports)
100
109
        hooks = Branch.hooks['open']
101
110
        for hook in hooks:
102
111
            hook(self)
103
112
 
104
 
    def _open_hook(self):
 
113
    def _open_hook(self, possible_transports):
105
114
        """Called by init to allow simpler extension of the base class."""
106
115
 
107
 
    def _activate_fallback_location(self, url):
 
116
    def _activate_fallback_location(self, url, possible_transports):
108
117
        """Activate the branch/repository from url as a fallback repository."""
109
118
        for existing_fallback_repo in self.repository._fallback_repositories:
110
119
            if existing_fallback_repo.user_url == url:
113
122
                # confusing _unstack we don't add this a second time.
114
123
                mutter('duplicate activation of fallback %r on %r', url, self)
115
124
                return
116
 
        repo = self._get_fallback_repository(url)
 
125
        repo = self._get_fallback_repository(url, possible_transports)
117
126
        if repo.has_same_location(self.repository):
118
127
            raise errors.UnstackableLocationError(self.user_url, url)
119
128
        self.repository.add_fallback_repository(repo)
173
182
        For instance, if the branch is at URL/.bzr/branch,
174
183
        Branch.open(URL) -> a Branch instance.
175
184
        """
176
 
        control = controldir.ControlDir.open(base, _unsupported,
177
 
                                     possible_transports=possible_transports)
178
 
        return control.open_branch(unsupported=_unsupported)
 
185
        control = controldir.ControlDir.open(base,
 
186
            possible_transports=possible_transports, _unsupported=_unsupported)
 
187
        return control.open_branch(unsupported=_unsupported,
 
188
            possible_transports=possible_transports)
179
189
 
180
190
    @staticmethod
181
 
    def open_from_transport(transport, name=None, _unsupported=False):
 
191
    def open_from_transport(transport, name=None, _unsupported=False,
 
192
            possible_transports=None):
182
193
        """Open the branch rooted at transport"""
183
194
        control = controldir.ControlDir.open_from_transport(transport, _unsupported)
184
 
        return control.open_branch(name=name, unsupported=_unsupported)
 
195
        return control.open_branch(name=name, unsupported=_unsupported,
 
196
            possible_transports=possible_transports)
185
197
 
186
198
    @staticmethod
187
199
    def open_containing(url, possible_transports=None):
197
209
        """
198
210
        control, relpath = controldir.ControlDir.open_containing(url,
199
211
                                                         possible_transports)
200
 
        return control.open_branch(), relpath
 
212
        branch = control.open_branch(possible_transports=possible_transports)
 
213
        return (branch, relpath)
201
214
 
202
215
    def _push_should_merge_tags(self):
203
216
        """Should _basic_push merge this branch's tags into the target?
239
252
        """
240
253
        raise NotImplementedError(self._get_config)
241
254
 
242
 
    def _get_fallback_repository(self, url):
 
255
    def store_uncommitted(self, creator):
 
256
        """Store uncommitted changes from a ShelfCreator.
 
257
 
 
258
        :param creator: The ShelfCreator containing uncommitted changes, or
 
259
            None to delete any stored changes.
 
260
        :raises: ChangesAlreadyStored if the branch already has changes.
 
261
        """
 
262
        raise NotImplementedError(self.store_uncommitted)
 
263
 
 
264
    def get_unshelver(self, tree):
 
265
        """Return a shelf.Unshelver for this branch and tree.
 
266
 
 
267
        :param tree: The tree to use to construct the Unshelver.
 
268
        :return: an Unshelver or None if no changes are stored.
 
269
        """
 
270
        raise NotImplementedError(self.get_unshelver)
 
271
 
 
272
    def _get_fallback_repository(self, url, possible_transports):
243
273
        """Get the repository we fallback to at url."""
244
274
        url = urlutils.join(self.base, url)
245
 
        a_branch = Branch.open(url,
246
 
            possible_transports=[self.bzrdir.root_transport])
 
275
        a_branch = Branch.open(url, possible_transports=possible_transports)
247
276
        return a_branch.repository
248
277
 
249
278
    @needs_read_lock
659
688
        """
660
689
        if not self._format.supports_set_append_revisions_only():
661
690
            return False
662
 
        return self.get_config(
663
 
            ).get_user_option_as_bool('append_revisions_only')
 
691
        return self.get_config_stack().get('append_revisions_only')
664
692
 
665
693
    def set_append_revisions_only(self, enabled):
666
694
        if not self._format.supports_set_append_revisions_only():
667
695
            raise errors.UpgradeRequired(self.user_url)
668
 
        if enabled:
669
 
            value = 'True'
670
 
        else:
671
 
            value = 'False'
672
 
        self.get_config().set_user_option('append_revisions_only', value,
673
 
            warn_masked=True)
 
696
        self.get_config_stack().set('append_revisions_only', enabled)
674
697
 
675
698
    def set_reference_info(self, file_id, tree_path, branch_location):
676
699
        """Set the branch location to use for a tree reference."""
705
728
        """
706
729
        raise errors.UpgradeRequired(self.user_url)
707
730
 
708
 
    def get_commit_builder(self, parents, config=None, timestamp=None,
 
731
    def get_commit_builder(self, parents, config_stack=None, timestamp=None,
709
732
                           timezone=None, committer=None, revprops=None,
710
733
                           revision_id=None, lossy=False):
711
734
        """Obtain a CommitBuilder for this branch.
721
744
            represented, when pushing to a foreign VCS 
722
745
        """
723
746
 
724
 
        if config is None:
725
 
            config = self.get_config()
 
747
        if config_stack is None:
 
748
            config_stack = self.get_config_stack()
726
749
 
727
 
        return self.repository.get_commit_builder(self, parents, config,
 
750
        return self.repository.get_commit_builder(self, parents, config_stack,
728
751
            timestamp, timezone, committer, revprops, revision_id,
729
752
            lossy)
730
753
 
761
784
        """Print `file` to stdout."""
762
785
        raise NotImplementedError(self.print_file)
763
786
 
764
 
    @deprecated_method(deprecated_in((2, 4, 0)))
765
 
    def set_revision_history(self, rev_history):
766
 
        """See Branch.set_revision_history."""
767
 
        self._set_revision_history(rev_history)
768
 
 
769
 
    @needs_write_lock
770
 
    def _set_revision_history(self, rev_history):
771
 
        if len(rev_history) == 0:
772
 
            revid = _mod_revision.NULL_REVISION
773
 
        else:
774
 
            revid = rev_history[-1]
775
 
        if rev_history != self._lefthand_history(revid):
776
 
            raise errors.NotLefthandHistory(rev_history)
777
 
        self.set_last_revision_info(len(rev_history), revid)
778
 
        self._cache_revision_history(rev_history)
779
 
        for hook in Branch.hooks['set_rh']:
780
 
            hook(self, rev_history)
781
 
 
782
787
    @needs_write_lock
783
788
    def set_last_revision_info(self, revno, revision_id):
784
789
        """Set the last revision of this branch.
851
856
                return
852
857
            self._unstack()
853
858
        else:
854
 
            self._activate_fallback_location(url)
 
859
            self._activate_fallback_location(url,
 
860
                possible_transports=[self.bzrdir.root_transport])
855
861
        # write this out after the repository is stacked to avoid setting a
856
862
        # stacked config that doesn't work.
857
863
        self._set_config_location('stacked_on_location', url)
933
939
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
934
940
                except errors.TagsNotSupported:
935
941
                    tags_to_fetch = set()
936
 
                fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
 
942
                fetch_spec = vf_search.NotInOtherForRevs(self.repository,
937
943
                    old_repository, required_ids=[self.last_revision()],
938
944
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
939
945
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
980
986
        This means the next call to revision_history will need to call
981
987
        _gen_revision_history.
982
988
 
983
 
        This API is semi-public; it only for use by subclasses, all other code
984
 
        should consider it to be private.
 
989
        This API is semi-public; it is only for use by subclasses, all other
 
990
        code should consider it to be private.
985
991
        """
986
992
        self._revision_history_cache = None
987
993
        self._revision_id_to_revno_cache = None
1007
1013
        """
1008
1014
        raise NotImplementedError(self._gen_revision_history)
1009
1015
 
1010
 
    @deprecated_method(deprecated_in((2, 5, 0)))
1011
 
    @needs_read_lock
1012
 
    def revision_history(self):
1013
 
        """Return sequence of revision ids on this branch.
1014
 
 
1015
 
        This method will cache the revision history for as long as it is safe to
1016
 
        do so.
1017
 
        """
1018
 
        return self._revision_history()
1019
 
 
1020
1016
    def _revision_history(self):
1021
1017
        if 'evil' in debug.debug_flags:
1022
1018
            mutter_callsite(3, "revision_history scales with history.")
1056
1052
    def _read_last_revision_info(self):
1057
1053
        raise NotImplementedError(self._read_last_revision_info)
1058
1054
 
1059
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1060
 
    def import_last_revision_info(self, source_repo, revno, revid):
1061
 
        """Set the last revision info, importing from another repo if necessary.
1062
 
 
1063
 
        :param source_repo: Source repository to optionally fetch from
1064
 
        :param revno: Revision number of the new tip
1065
 
        :param revid: Revision id of the new tip
1066
 
        """
1067
 
        if not self.repository.has_same_location(source_repo):
1068
 
            self.repository.fetch(source_repo, revision_id=revid)
1069
 
        self.set_last_revision_info(revno, revid)
1070
 
 
1071
1055
    def import_last_revision_info_and_tags(self, source, revno, revid,
1072
1056
                                           lossy=False):
1073
1057
        """Set the last revision info, importing from another repo if necessary.
1164
1148
    def _set_config_location(self, name, url, config=None,
1165
1149
                             make_relative=False):
1166
1150
        if config is None:
1167
 
            config = self.get_config()
 
1151
            config = self.get_config_stack()
1168
1152
        if url is None:
1169
1153
            url = ''
1170
1154
        elif make_relative:
1171
1155
            url = urlutils.relative_url(self.base, url)
1172
 
        config.set_user_option(name, url, warn_masked=True)
 
1156
        config.set(name, url)
1173
1157
 
1174
1158
    def _get_config_location(self, name, config=None):
1175
1159
        if config is None:
1176
 
            config = self.get_config()
1177
 
        location = config.get_user_option(name)
 
1160
            config = self.get_config_stack()
 
1161
        location = config.get(name)
1178
1162
        if location == '':
1179
1163
            location = None
1180
1164
        return location
1181
1165
 
1182
1166
    def get_child_submit_format(self):
1183
1167
        """Return the preferred format of submissions to this branch."""
1184
 
        return self.get_config().get_user_option("child_submit_format")
 
1168
        return self.get_config_stack().get('child_submit_format')
1185
1169
 
1186
1170
    def get_submit_branch(self):
1187
1171
        """Return the submit location of the branch.
1190
1174
        pattern is that the user can override it by specifying a
1191
1175
        location.
1192
1176
        """
1193
 
        return self.get_config().get_user_option('submit_branch')
 
1177
        return self.get_config_stack().get('submit_branch')
1194
1178
 
1195
1179
    def set_submit_branch(self, location):
1196
1180
        """Return the submit location of the branch.
1199
1183
        pattern is that the user can override it by specifying a
1200
1184
        location.
1201
1185
        """
1202
 
        self.get_config().set_user_option('submit_branch', location,
1203
 
            warn_masked=True)
 
1186
        self.get_config_stack().set('submit_branch', location)
1204
1187
 
1205
1188
    def get_public_branch(self):
1206
1189
        """Return the public location of the branch.
1219
1202
        self._set_config_location('public_branch', location)
1220
1203
 
1221
1204
    def get_push_location(self):
1222
 
        """Return the None or the location to push this branch to."""
1223
 
        push_loc = self.get_config().get_user_option('push_location')
1224
 
        return push_loc
 
1205
        """Return None or the location to push this branch to."""
 
1206
        return self.get_config_stack().get('push_location')
1225
1207
 
1226
1208
    def set_push_location(self, location):
1227
1209
        """Set a new push location for this branch."""
1453
1435
        t = transport.get_transport(to_location)
1454
1436
        t.ensure_base()
1455
1437
        format = self._get_checkout_format(lightweight=lightweight)
 
1438
        try:
 
1439
            checkout = format.initialize_on_transport(t)
 
1440
        except errors.AlreadyControlDirError:
 
1441
            # It's fine if the control directory already exists,
 
1442
            # as long as there is no existing branch and working tree.
 
1443
            checkout = controldir.ControlDir.open_from_transport(t)
 
1444
            try:
 
1445
                checkout.open_branch()
 
1446
            except errors.NotBranchError:
 
1447
                pass
 
1448
            else:
 
1449
                raise errors.AlreadyControlDirError(t.base)
 
1450
            if checkout.control_transport.base == self.bzrdir.control_transport.base:
 
1451
                # When checking out to the same control directory,
 
1452
                # always create a lightweight checkout
 
1453
                lightweight = True
 
1454
 
1456
1455
        if lightweight:
1457
 
            checkout = format.initialize_on_transport(t)
1458
 
            from_branch = BranchReferenceFormat().initialize(checkout, 
1459
 
                target_branch=self)
 
1456
            from_branch = checkout.set_branch_reference(target_branch=self)
1460
1457
        else:
1461
 
            checkout_branch = controldir.ControlDir.create_branch_convenience(
1462
 
                to_location, force_new_tree=False, format=format)
1463
 
            checkout = checkout_branch.bzrdir
 
1458
            policy = checkout.determine_repository_policy()
 
1459
            repo = policy.acquire_repository()[0]
 
1460
            checkout_branch = checkout.create_branch()
1464
1461
            checkout_branch.bind(self)
1465
1462
            # pull up to the specified revision_id to set the initial
1466
1463
            # branch tip correctly, and seed it with history.
1467
1464
            checkout_branch.pull(self, stop_revision=revision_id)
1468
 
            from_branch=None
 
1465
            from_branch = None
1469
1466
        tree = checkout.create_workingtree(revision_id,
1470
1467
                                           from_branch=from_branch,
1471
1468
                                           accelerator_tree=accelerator_tree,
1560
1557
            heads that must be fetched if present, but no error is necessary if
1561
1558
            they are not present.
1562
1559
        """
1563
 
        # For bzr native formats must_fetch is just the tip, and if_present_fetch
1564
 
        # are the tags.
 
1560
        # For bzr native formats must_fetch is just the tip, and
 
1561
        # if_present_fetch are the tags.
1565
1562
        must_fetch = set([self.last_revision()])
1566
1563
        if_present_fetch = set()
1567
 
        c = self.get_config()
1568
 
        include_tags = c.get_user_option_as_bool('branch.fetch_tags',
1569
 
                                                 default=False)
1570
 
        if include_tags:
 
1564
        if self.get_config_stack().get('branch.fetch_tags'):
1571
1565
            try:
1572
1566
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
1573
1567
            except errors.TagsNotSupported:
1582
1576
 
1583
1577
    Formats provide three things:
1584
1578
     * An initialization routine,
1585
 
     * a format string,
 
1579
     * a format description
1586
1580
     * an open routine.
1587
1581
 
1588
1582
    Formats are placed in an dict by their format string for reference
1601
1595
    def __ne__(self, other):
1602
1596
        return not (self == other)
1603
1597
 
1604
 
    @classmethod
1605
 
    def find_format(klass, controldir, name=None):
1606
 
        """Return the format for the branch object in controldir."""
1607
 
        try:
1608
 
            transport = controldir.get_branch_transport(None, name=name)
1609
 
            format_string = transport.get_bytes("format")
1610
 
            return format_registry.get(format_string)
1611
 
        except errors.NoSuchFile:
1612
 
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1613
 
        except KeyError:
1614
 
            raise errors.UnknownFormatError(format=format_string, kind='branch')
1615
 
 
1616
 
    @classmethod
1617
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1618
 
    def get_default_format(klass):
1619
 
        """Return the current default format."""
1620
 
        return format_registry.get_default()
1621
 
 
1622
 
    @classmethod
1623
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1624
 
    def get_formats(klass):
1625
 
        """Get all the known formats.
1626
 
 
1627
 
        Warning: This triggers a load of all lazy registered formats: do not
1628
 
        use except when that is desireed.
1629
 
        """
1630
 
        return format_registry._get_all()
1631
 
 
1632
1598
    def get_reference(self, controldir, name=None):
1633
1599
        """Get the target reference of the branch in controldir.
1634
1600
 
1656
1622
        """
1657
1623
        raise NotImplementedError(self.set_reference)
1658
1624
 
1659
 
    def get_format_string(self):
1660
 
        """Return the ASCII format string that identifies this format."""
1661
 
        raise NotImplementedError(self.get_format_string)
1662
 
 
1663
1625
    def get_format_description(self):
1664
1626
        """Return the short format description for this format."""
1665
1627
        raise NotImplementedError(self.get_format_description)
1714
1676
        raise NotImplementedError(self.network_name)
1715
1677
 
1716
1678
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
1717
 
            found_repository=None):
 
1679
            found_repository=None, possible_transports=None):
1718
1680
        """Return the branch object for controldir.
1719
1681
 
1720
1682
        :param controldir: A ControlDir that contains a branch.
1726
1688
        """
1727
1689
        raise NotImplementedError(self.open)
1728
1690
 
1729
 
    @classmethod
1730
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1731
 
    def register_format(klass, format):
1732
 
        """Register a metadir format.
1733
 
 
1734
 
        See MetaDirBranchFormatFactory for the ability to register a format
1735
 
        without loading the code the format needs until it is actually used.
1736
 
        """
1737
 
        format_registry.register(format)
1738
 
 
1739
 
    @classmethod
1740
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1741
 
    def set_default_format(klass, format):
1742
 
        format_registry.set_default(format)
1743
 
 
1744
1691
    def supports_set_append_revisions_only(self):
1745
1692
        """True if this format supports set_append_revisions_only."""
1746
1693
        return False
1753
1700
        """True if this format supports leaving locks in place."""
1754
1701
        return False # by default
1755
1702
 
1756
 
    @classmethod
1757
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1758
 
    def unregister_format(klass, format):
1759
 
        format_registry.remove(format)
1760
 
 
1761
1703
    def __str__(self):
1762
1704
        return self.get_format_description().rstrip()
1763
1705
 
1792
1734
        """
1793
1735
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
1794
1736
        self._format_string = format_string
1795
 
        
 
1737
 
1796
1738
    def get_format_string(self):
1797
1739
        """See BranchFormat.get_format_string."""
1798
1740
        return self._format_string
1805
1747
class BranchHooks(Hooks):
1806
1748
    """A dictionary mapping hook name to a list of callables for branch hooks.
1807
1749
 
1808
 
    e.g. ['set_rh'] Is the list of items to be called when the
1809
 
    set_revision_history function is invoked.
 
1750
    e.g. ['post_push'] Is the list of items to be called when the
 
1751
    push function is invoked.
1810
1752
    """
1811
1753
 
1812
1754
    def __init__(self):
1816
1758
        notified.
1817
1759
        """
1818
1760
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1819
 
        self.add_hook('set_rh',
1820
 
            "Invoked whenever the revision history has been set via "
1821
 
            "set_revision_history. The api signature is (branch, "
1822
 
            "revision_history), and the branch will be write-locked. "
1823
 
            "The set_rh hook can be expensive for bzr to trigger, a better "
1824
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
1825
1761
        self.add_hook('open',
1826
1762
            "Called with the Branch object that has been opened after a "
1827
1763
            "branch is opened.", (1, 8))
2010
1946
            self.revision_id)
2011
1947
 
2012
1948
 
2013
 
class BranchFormatMetadir(BranchFormat):
2014
 
    """Common logic for meta-dir based branch formats."""
 
1949
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
 
1950
    """Base class for branch formats that live in meta directories.
 
1951
    """
 
1952
 
 
1953
    def __init__(self):
 
1954
        BranchFormat.__init__(self)
 
1955
        bzrdir.BzrFormat.__init__(self)
 
1956
 
 
1957
    @classmethod
 
1958
    def find_format(klass, controldir, name=None):
 
1959
        """Return the format for the branch object in controldir."""
 
1960
        try:
 
1961
            transport = controldir.get_branch_transport(None, name=name)
 
1962
        except errors.NoSuchFile:
 
1963
            raise errors.NotBranchError(path=name, bzrdir=controldir)
 
1964
        try:
 
1965
            format_string = transport.get_bytes("format")
 
1966
        except errors.NoSuchFile:
 
1967
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
 
1968
        return klass._find_format(format_registry, 'branch', format_string)
2015
1969
 
2016
1970
    def _branch_class(self):
2017
1971
        """What class to instantiate on open calls."""
2027
1981
 
2028
1982
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
2029
1983
                           repository=None):
2030
 
        """Initialize a branch in a bzrdir, with specified files
 
1984
        """Initialize a branch in a control dir, with specified files
2031
1985
 
2032
1986
        :param a_bzrdir: The bzrdir to initialize the branch in
2033
1987
        :param utf8_files: The files to create as a list of
2035
1989
        :param name: Name of colocated branch to create, if any
2036
1990
        :return: a branch in this format
2037
1991
        """
 
1992
        if name is None:
 
1993
            name = a_bzrdir._get_selected_branch()
2038
1994
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
2039
1995
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2040
1996
        control_files = lockable_files.LockableFiles(branch_transport,
2042
1998
        control_files.create_lock()
2043
1999
        control_files.lock_write()
2044
2000
        try:
2045
 
            utf8_files += [('format', self.get_format_string())]
 
2001
            utf8_files += [('format', self.as_string())]
2046
2002
            for (filename, content) in utf8_files:
2047
2003
                branch_transport.put_bytes(
2048
2004
                    filename, content,
2054
2010
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2055
2011
        return branch
2056
2012
 
2057
 
    def network_name(self):
2058
 
        """A simple byte string uniquely identifying this format for RPC calls.
2059
 
 
2060
 
        Metadir branch formats use their format string.
2061
 
        """
2062
 
        return self.get_format_string()
2063
 
 
2064
2013
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2065
 
            found_repository=None):
 
2014
            found_repository=None, possible_transports=None):
2066
2015
        """See BranchFormat.open()."""
 
2016
        if name is None:
 
2017
            name = a_bzrdir._get_selected_branch()
2067
2018
        if not _found:
2068
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2019
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2069
2020
            if format.__class__ != self.__class__:
2070
2021
                raise AssertionError("wrong format %r found for %r" %
2071
2022
                    (format, self))
2080
2031
                              name=name,
2081
2032
                              a_bzrdir=a_bzrdir,
2082
2033
                              _repository=found_repository,
2083
 
                              ignore_fallbacks=ignore_fallbacks)
 
2034
                              ignore_fallbacks=ignore_fallbacks,
 
2035
                              possible_transports=possible_transports)
2084
2036
        except errors.NoSuchFile:
2085
2037
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2086
2038
 
2096
2048
    def supports_leaving_lock(self):
2097
2049
        return True
2098
2050
 
2099
 
 
2100
 
class BzrBranchFormat5(BranchFormatMetadir):
2101
 
    """Bzr branch format 5.
2102
 
 
2103
 
    This format has:
2104
 
     - a revision-history file.
2105
 
     - a format string
2106
 
     - a lock dir guarding the branch itself
2107
 
     - all of this stored in a branch/ subdirectory
2108
 
     - works with shared repositories.
2109
 
 
2110
 
    This format is new in bzr 0.8.
2111
 
    """
2112
 
 
2113
 
    def _branch_class(self):
2114
 
        return BzrBranch5
2115
 
 
2116
 
    def get_format_string(self):
2117
 
        """See BranchFormat.get_format_string()."""
2118
 
        return "Bazaar-NG branch format 5\n"
2119
 
 
2120
 
    def get_format_description(self):
2121
 
        """See BranchFormat.get_format_description()."""
2122
 
        return "Branch format 5"
2123
 
 
2124
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2125
 
                   append_revisions_only=None):
2126
 
        """Create a branch of this format in a_bzrdir."""
2127
 
        if append_revisions_only:
2128
 
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2129
 
        utf8_files = [('revision-history', ''),
2130
 
                      ('branch-name', ''),
2131
 
                      ]
2132
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2133
 
 
2134
 
    def supports_tags(self):
2135
 
        return False
 
2051
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
2052
            basedir=None):
 
2053
        BranchFormat.check_support_status(self,
 
2054
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
 
2055
            basedir=basedir)
 
2056
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
 
2057
            recommend_upgrade=recommend_upgrade, basedir=basedir)
2136
2058
 
2137
2059
 
2138
2060
class BzrBranchFormat6(BranchFormatMetadir):
2149
2071
    def _branch_class(self):
2150
2072
        return BzrBranch6
2151
2073
 
2152
 
    def get_format_string(self):
 
2074
    @classmethod
 
2075
    def get_format_string(cls):
2153
2076
        """See BranchFormat.get_format_string()."""
2154
2077
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
2155
2078
 
2181
2104
    def _branch_class(self):
2182
2105
        return BzrBranch8
2183
2106
 
2184
 
    def get_format_string(self):
 
2107
    @classmethod
 
2108
    def get_format_string(cls):
2185
2109
        """See BranchFormat.get_format_string()."""
2186
2110
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2187
2111
 
2235
2159
    def _branch_class(self):
2236
2160
        return BzrBranch7
2237
2161
 
2238
 
    def get_format_string(self):
 
2162
    @classmethod
 
2163
    def get_format_string(cls):
2239
2164
        """See BranchFormat.get_format_string()."""
2240
2165
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2241
2166
 
2267
2192
     - a format string
2268
2193
    """
2269
2194
 
2270
 
    def get_format_string(self):
 
2195
    @classmethod
 
2196
    def get_format_string(cls):
2271
2197
        """See BranchFormat.get_format_string()."""
2272
2198
        return "Bazaar-NG Branch Reference Format 1\n"
2273
2199
 
2295
2221
        mutter('creating branch reference in %s', a_bzrdir.user_url)
2296
2222
        if a_bzrdir._format.fixed_components:
2297
2223
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
 
2224
        if name is None:
 
2225
            name = a_bzrdir._get_selected_branch()
2298
2226
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2299
2227
        branch_transport.put_bytes('location',
2300
 
            target_branch.bzrdir.user_url)
2301
 
        branch_transport.put_bytes('format', self.get_format_string())
2302
 
        branch = self.open(
2303
 
            a_bzrdir, name, _found=True,
 
2228
            target_branch.user_url)
 
2229
        branch_transport.put_bytes('format', self.as_string())
 
2230
        branch = self.open(a_bzrdir, name, _found=True,
2304
2231
            possible_transports=[target_branch.bzrdir.root_transport])
2305
2232
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2306
2233
        return branch
2332
2259
            a_bzrdir.
2333
2260
        :param possible_transports: An optional reusable transports list.
2334
2261
        """
 
2262
        if name is None:
 
2263
            name = a_bzrdir._get_selected_branch()
2335
2264
        if not _found:
2336
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2265
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2337
2266
            if format.__class__ != self.__class__:
2338
2267
                raise AssertionError("wrong format %r found for %r" %
2339
2268
                    (format, self))
2341
2270
            location = self.get_reference(a_bzrdir, name)
2342
2271
        real_bzrdir = controldir.ControlDir.open(
2343
2272
            location, possible_transports=possible_transports)
2344
 
        result = real_bzrdir.open_branch(name=name, 
2345
 
            ignore_fallbacks=ignore_fallbacks)
 
2273
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
 
2274
            possible_transports=possible_transports)
2346
2275
        # this changes the behaviour of result.clone to create a new reference
2347
2276
        # rather than a copy of the content of the branch.
2348
2277
        # I did not use a proxy object because that needs much more extensive
2382
2311
 
2383
2312
# formats which have no format string are not discoverable
2384
2313
# and not independently creatable, so are not registered.
2385
 
__format5 = BzrBranchFormat5()
2386
2314
__format6 = BzrBranchFormat6()
2387
2315
__format7 = BzrBranchFormat7()
2388
2316
__format8 = BzrBranchFormat8()
2389
 
format_registry.register(__format5)
 
2317
format_registry.register_lazy(
 
2318
    "Bazaar-NG branch format 5\n", "bzrlib.branchfmt.fullhistory", "BzrBranchFormat5")
2390
2319
format_registry.register(BranchReferenceFormat())
2391
2320
format_registry.register(__format6)
2392
2321
format_registry.register(__format7)
2429
2358
 
2430
2359
    def __init__(self, _format=None,
2431
2360
                 _control_files=None, a_bzrdir=None, name=None,
2432
 
                 _repository=None, ignore_fallbacks=False):
 
2361
                 _repository=None, ignore_fallbacks=False,
 
2362
                 possible_transports=None):
2433
2363
        """Create new branch object at a particular location."""
2434
2364
        if a_bzrdir is None:
2435
2365
            raise ValueError('a_bzrdir must be supplied')
2436
 
        else:
2437
 
            self.bzrdir = a_bzrdir
2438
 
        self._base = self.bzrdir.transport.clone('..').base
 
2366
        if name is None:
 
2367
            raise ValueError('name must be supplied')
 
2368
        self.bzrdir = a_bzrdir
 
2369
        self._user_transport = self.bzrdir.transport.clone('..')
 
2370
        if name != "":
 
2371
            self._user_transport.set_segment_parameter(
 
2372
                "branch", urlutils.escape(name))
 
2373
        self._base = self._user_transport.base
2439
2374
        self.name = name
2440
 
        # XXX: We should be able to just do
2441
 
        #   self.base = self.bzrdir.root_transport.base
2442
 
        # but this does not quite work yet -- mbp 20080522
2443
2375
        self._format = _format
2444
2376
        if _control_files is None:
2445
2377
            raise ValueError('BzrBranch _control_files is None')
2446
2378
        self.control_files = _control_files
2447
2379
        self._transport = _control_files._transport
2448
2380
        self.repository = _repository
2449
 
        Branch.__init__(self)
 
2381
        self.conf_store = None
 
2382
        Branch.__init__(self, possible_transports)
2450
2383
 
2451
2384
    def __str__(self):
2452
 
        if self.name is None:
2453
 
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
2454
 
        else:
2455
 
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2456
 
                self.name)
 
2385
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
2457
2386
 
2458
2387
    __repr__ = __str__
2459
2388
 
2463
2392
 
2464
2393
    base = property(_get_base, doc="The URL for the root of this branch.")
2465
2394
 
 
2395
    @property
 
2396
    def user_transport(self):
 
2397
        return self._user_transport
 
2398
 
2466
2399
    def _get_config(self):
2467
2400
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
2468
2401
 
 
2402
    def _get_config_store(self):
 
2403
        if self.conf_store is None:
 
2404
            self.conf_store =  _mod_config.BranchStore(self)
 
2405
        return self.conf_store
 
2406
 
 
2407
    def _uncommitted_branch(self):
 
2408
        """Return the branch that may contain uncommitted changes."""
 
2409
        master = self.get_master_branch()
 
2410
        if master is not None:
 
2411
            return master
 
2412
        else:
 
2413
            return self
 
2414
 
 
2415
    def store_uncommitted(self, creator):
 
2416
        """Store uncommitted changes from a ShelfCreator.
 
2417
 
 
2418
        :param creator: The ShelfCreator containing uncommitted changes, or
 
2419
            None to delete any stored changes.
 
2420
        :raises: ChangesAlreadyStored if the branch already has changes.
 
2421
        """
 
2422
        branch = self._uncommitted_branch()
 
2423
        if creator is None:
 
2424
            branch._transport.delete('stored-transform')
 
2425
            return
 
2426
        if branch._transport.has('stored-transform'):
 
2427
            raise errors.ChangesAlreadyStored
 
2428
        transform = StringIO()
 
2429
        creator.write_shelf(transform)
 
2430
        transform.seek(0)
 
2431
        branch._transport.put_file('stored-transform', transform)
 
2432
 
 
2433
    def get_unshelver(self, tree):
 
2434
        """Return a shelf.Unshelver for this branch and tree.
 
2435
 
 
2436
        :param tree: The tree to use to construct the Unshelver.
 
2437
        :return: an Unshelver or None if no changes are stored.
 
2438
        """
 
2439
        branch = self._uncommitted_branch()
 
2440
        try:
 
2441
            transform = branch._transport.get('stored-transform')
 
2442
        except errors.NoSuchFile:
 
2443
            return None
 
2444
        return shelf.Unshelver.from_tree_and_shelf(tree, transform)
 
2445
 
2469
2446
    def is_locked(self):
2470
2447
        return self.control_files.is_locked()
2471
2448
 
2478
2455
        """
2479
2456
        if not self.is_locked():
2480
2457
            self._note_lock('w')
2481
 
        # All-in-one needs to always unlock/lock.
2482
 
        repo_control = getattr(self.repository, 'control_files', None)
2483
 
        if self.control_files == repo_control or not self.is_locked():
2484
2458
            self.repository._warn_if_deprecated(self)
2485
2459
            self.repository.lock_write()
2486
2460
            took_lock = True
2501
2475
        """
2502
2476
        if not self.is_locked():
2503
2477
            self._note_lock('r')
2504
 
        # All-in-one needs to always unlock/lock.
2505
 
        repo_control = getattr(self.repository, 'control_files', None)
2506
 
        if self.control_files == repo_control or not self.is_locked():
2507
2478
            self.repository._warn_if_deprecated(self)
2508
2479
            self.repository.lock_read()
2509
2480
            took_lock = True
2519
2490
 
2520
2491
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2521
2492
    def unlock(self):
 
2493
        if self.control_files._lock_count == 1 and self.conf_store is not None:
 
2494
            self.conf_store.save_changes()
2522
2495
        try:
2523
2496
            self.control_files.unlock()
2524
2497
        finally:
2525
 
            # All-in-one needs to always unlock/lock.
2526
 
            repo_control = getattr(self.repository, 'control_files', None)
2527
 
            if (self.control_files == repo_control or
2528
 
                not self.control_files.is_locked()):
2529
 
                self.repository.unlock()
2530
2498
            if not self.control_files.is_locked():
 
2499
                self.repository.unlock()
2531
2500
                # we just released the lock
2532
2501
                self._clear_cached_state()
2533
2502
 
2700
2669
        self._transport.put_bytes('last-revision', out_string,
2701
2670
            mode=self.bzrdir._get_file_mode())
2702
2671
 
2703
 
 
2704
 
class FullHistoryBzrBranch(BzrBranch):
2705
 
    """Bzr branch which contains the full revision history."""
2706
 
 
2707
 
    @needs_write_lock
2708
 
    def set_last_revision_info(self, revno, revision_id):
2709
 
        if not revision_id or not isinstance(revision_id, basestring):
2710
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2711
 
        revision_id = _mod_revision.ensure_null(revision_id)
2712
 
        # this old format stores the full history, but this api doesn't
2713
 
        # provide it, so we must generate, and might as well check it's
2714
 
        # correct
2715
 
        history = self._lefthand_history(revision_id)
2716
 
        if len(history) != revno:
2717
 
            raise AssertionError('%d != %d' % (len(history), revno))
2718
 
        self._set_revision_history(history)
2719
 
 
2720
 
    def _read_last_revision_info(self):
2721
 
        rh = self._revision_history()
2722
 
        revno = len(rh)
2723
 
        if revno:
2724
 
            return (revno, rh[-1])
2725
 
        else:
2726
 
            return (0, _mod_revision.NULL_REVISION)
2727
 
 
2728
 
    @deprecated_method(deprecated_in((2, 4, 0)))
2729
 
    @needs_write_lock
2730
 
    def set_revision_history(self, rev_history):
2731
 
        """See Branch.set_revision_history."""
2732
 
        self._set_revision_history(rev_history)
2733
 
 
2734
 
    def _set_revision_history(self, rev_history):
2735
 
        if 'evil' in debug.debug_flags:
2736
 
            mutter_callsite(3, "set_revision_history scales with history.")
2737
 
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2738
 
        for rev_id in rev_history:
2739
 
            check_not_reserved_id(rev_id)
2740
 
        if Branch.hooks['post_change_branch_tip']:
2741
 
            # Don't calculate the last_revision_info() if there are no hooks
2742
 
            # that will use it.
2743
 
            old_revno, old_revid = self.last_revision_info()
2744
 
        if len(rev_history) == 0:
2745
 
            revid = _mod_revision.NULL_REVISION
2746
 
        else:
2747
 
            revid = rev_history[-1]
2748
 
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2749
 
        self._write_revision_history(rev_history)
2750
 
        self._clear_cached_state()
2751
 
        self._cache_revision_history(rev_history)
2752
 
        for hook in Branch.hooks['set_rh']:
2753
 
            hook(self, rev_history)
2754
 
        if Branch.hooks['post_change_branch_tip']:
2755
 
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2756
 
 
2757
 
    def _write_revision_history(self, history):
2758
 
        """Factored out of set_revision_history.
2759
 
 
2760
 
        This performs the actual writing to disk.
2761
 
        It is intended to be called by set_revision_history."""
2762
 
        self._transport.put_bytes(
2763
 
            'revision-history', '\n'.join(history),
2764
 
            mode=self.bzrdir._get_file_mode())
2765
 
 
2766
 
    def _gen_revision_history(self):
2767
 
        history = self._transport.get_bytes('revision-history').split('\n')
2768
 
        if history[-1:] == ['']:
2769
 
            # There shouldn't be a trailing newline, but just in case.
2770
 
            history.pop()
2771
 
        return history
2772
 
 
2773
 
    def _synchronize_history(self, destination, revision_id):
2774
 
        if not isinstance(destination, FullHistoryBzrBranch):
2775
 
            super(BzrBranch, self)._synchronize_history(
2776
 
                destination, revision_id)
2777
 
            return
2778
 
        if revision_id == _mod_revision.NULL_REVISION:
2779
 
            new_history = []
2780
 
        else:
2781
 
            new_history = self._revision_history()
2782
 
        if revision_id is not None and new_history != []:
2783
 
            try:
2784
 
                new_history = new_history[:new_history.index(revision_id) + 1]
2785
 
            except ValueError:
2786
 
                rev = self.repository.get_revision(revision_id)
2787
 
                new_history = rev.get_history(self.repository)[1:]
2788
 
        destination._set_revision_history(new_history)
2789
 
 
2790
 
    @needs_write_lock
2791
 
    def generate_revision_history(self, revision_id, last_rev=None,
2792
 
        other_branch=None):
2793
 
        """Create a new revision history that will finish with revision_id.
2794
 
 
2795
 
        :param revision_id: the new tip to use.
2796
 
        :param last_rev: The previous last_revision. If not None, then this
2797
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
2798
 
        :param other_branch: The other branch that DivergedBranches should
2799
 
            raise with respect to.
 
2672
    @needs_write_lock
 
2673
    def update_feature_flags(self, updated_flags):
 
2674
        """Update the feature flags for this branch.
 
2675
 
 
2676
        :param updated_flags: Dictionary mapping feature names to necessities
 
2677
            A necessity can be None to indicate the feature should be removed
2800
2678
        """
2801
 
        self._set_revision_history(self._lefthand_history(revision_id,
2802
 
            last_rev, other_branch))
2803
 
 
2804
 
 
2805
 
class BzrBranch5(FullHistoryBzrBranch):
2806
 
    """A format 5 branch. This supports new features over plain branches.
2807
 
 
2808
 
    It has support for a master_branch which is the data for bound branches.
2809
 
    """
 
2679
        self._format._update_feature_flags(updated_flags)
 
2680
        self.control_transport.put_bytes('format', self._format.as_string())
2810
2681
 
2811
2682
 
2812
2683
class BzrBranch8(BzrBranch):
2813
2684
    """A branch that stores tree-reference locations."""
2814
2685
 
2815
 
    def _open_hook(self):
 
2686
    def _open_hook(self, possible_transports=None):
2816
2687
        if self._ignore_fallbacks:
2817
2688
            return
 
2689
        if possible_transports is None:
 
2690
            possible_transports = [self.bzrdir.root_transport]
2818
2691
        try:
2819
2692
            url = self.get_stacked_on_url()
2820
2693
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2828
2701
                    raise AssertionError(
2829
2702
                        "'transform_fallback_location' hook %s returned "
2830
2703
                        "None, not a URL." % hook_name)
2831
 
            self._activate_fallback_location(url)
 
2704
            self._activate_fallback_location(url,
 
2705
                possible_transports=possible_transports)
2832
2706
 
2833
2707
    def __init__(self, *args, **kwargs):
2834
2708
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2952
2826
        """See Branch.set_push_location."""
2953
2827
        self._master_branch_cache = None
2954
2828
        result = None
2955
 
        config = self.get_config()
 
2829
        conf = self.get_config_stack()
2956
2830
        if location is None:
2957
 
            if config.get_user_option('bound') != 'True':
 
2831
            if not conf.get('bound'):
2958
2832
                return False
2959
2833
            else:
2960
 
                config.set_user_option('bound', 'False', warn_masked=True)
 
2834
                conf.set('bound', 'False')
2961
2835
                return True
2962
2836
        else:
2963
2837
            self._set_config_location('bound_location', location,
2964
 
                                      config=config)
2965
 
            config.set_user_option('bound', 'True', warn_masked=True)
 
2838
                                      config=conf)
 
2839
            conf.set('bound', 'True')
2966
2840
        return True
2967
2841
 
2968
2842
    def _get_bound_location(self, bound):
2969
2843
        """Return the bound location in the config file.
2970
2844
 
2971
2845
        Return None if the bound parameter does not match"""
2972
 
        config = self.get_config()
2973
 
        config_bound = (config.get_user_option('bound') == 'True')
2974
 
        if config_bound != bound:
 
2846
        conf = self.get_config_stack()
 
2847
        if conf.get('bound') != bound:
2975
2848
            return None
2976
 
        return self._get_config_location('bound_location', config=config)
 
2849
        return self._get_config_location('bound_location', config=conf)
2977
2850
 
2978
2851
    def get_bound_location(self):
2979
 
        """See Branch.set_push_location."""
 
2852
        """See Branch.get_bound_location."""
2980
2853
        return self._get_bound_location(True)
2981
2854
 
2982
2855
    def get_old_bound_location(self):
2989
2862
        ## self._check_stackable_repo()
2990
2863
        # stacked_on_location is only ever defined in branch.conf, so don't
2991
2864
        # waste effort reading the whole stack of config files.
2992
 
        config = self.get_config()._get_branch_data_config()
 
2865
        conf = _mod_config.BranchOnlyStack(self)
2993
2866
        stacked_url = self._get_config_location('stacked_on_location',
2994
 
            config=config)
 
2867
                                                config=conf)
2995
2868
        if stacked_url is None:
2996
2869
            raise errors.NotStacked(self)
2997
 
        return stacked_url
 
2870
        return stacked_url.encode('utf-8')
2998
2871
 
2999
2872
    @needs_read_lock
3000
2873
    def get_rev_id(self, revno, history=None):
3030
2903
            except errors.RevisionNotPresent, e:
3031
2904
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
3032
2905
            index = len(self._partial_revision_history_cache) - 1
 
2906
            if index < 0:
 
2907
                raise errors.NoSuchRevision(self, revision_id)
3033
2908
            if self._partial_revision_history_cache[index] != revision_id:
3034
2909
                raise errors.NoSuchRevision(self, revision_id)
3035
2910
        return self.revno() - index
3090
2965
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3091
2966
    """
3092
2967
 
3093
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3094
 
    def __int__(self):
3095
 
        """Return the relative change in revno.
3096
 
 
3097
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3098
 
        """
3099
 
        return self.new_revno - self.old_revno
3100
 
 
3101
2968
    def report(self, to_file):
3102
2969
        tag_conflicts = getattr(self, "tag_conflicts", None)
3103
2970
        tag_updates = getattr(self, "tag_updates", None)
3133
3000
        target, otherwise it will be None.
3134
3001
    """
3135
3002
 
3136
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3137
 
    def __int__(self):
3138
 
        """Return the relative change in revno.
3139
 
 
3140
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3141
 
        """
3142
 
        return self.new_revno - self.old_revno
3143
 
 
3144
3003
    def report(self, to_file):
3145
3004
        # TODO: This function gets passed a to_file, but then
3146
3005
        # ignores it and calls note() instead. This is also
3193
3052
 
3194
3053
        # Copy source data into target
3195
3054
        new_branch._write_last_revision_info(*branch.last_revision_info())
3196
 
        new_branch.set_parent(branch.get_parent())
3197
 
        new_branch.set_bound_location(branch.get_bound_location())
3198
 
        new_branch.set_push_location(branch.get_push_location())
 
3055
        new_branch.lock_write()
 
3056
        try:
 
3057
            new_branch.set_parent(branch.get_parent())
 
3058
            new_branch.set_bound_location(branch.get_bound_location())
 
3059
            new_branch.set_push_location(branch.get_push_location())
 
3060
        finally:
 
3061
            new_branch.unlock()
3199
3062
 
3200
3063
        # New branch has no tags by default
3201
3064
        new_branch.tags._set_tag_dict({})
3202
3065
 
3203
3066
        # Copying done; now update target format
3204
3067
        new_branch._transport.put_bytes('format',
3205
 
            format.get_format_string(),
 
3068
            format.as_string(),
3206
3069
            mode=new_branch.bzrdir._get_file_mode())
3207
3070
 
3208
3071
        # Clean up old files
3209
3072
        new_branch._transport.delete('revision-history')
 
3073
        branch.lock_write()
3210
3074
        try:
3211
 
            branch.set_parent(None)
3212
 
        except errors.NoSuchFile:
3213
 
            pass
3214
 
        branch.set_bound_location(None)
 
3075
            try:
 
3076
                branch.set_parent(None)
 
3077
            except errors.NoSuchFile:
 
3078
                pass
 
3079
            branch.set_bound_location(None)
 
3080
        finally:
 
3081
            branch.unlock()
3215
3082
 
3216
3083
 
3217
3084
class Converter6to7(object):
3221
3088
        format = BzrBranchFormat7()
3222
3089
        branch._set_config_location('stacked_on_location', '')
3223
3090
        # update target format
3224
 
        branch._transport.put_bytes('format', format.get_format_string())
 
3091
        branch._transport.put_bytes('format', format.as_string())
3225
3092
 
3226
3093
 
3227
3094
class Converter7to8(object):
3231
3098
        format = BzrBranchFormat8()
3232
3099
        branch._transport.put_bytes('references', '')
3233
3100
        # update target format
3234
 
        branch._transport.put_bytes('format', format.get_format_string())
 
3101
        branch._transport.put_bytes('format', format.as_string())
3235
3102
 
3236
3103
 
3237
3104
class InterBranch(InterObject):
3294
3161
        raise NotImplementedError(self.fetch)
3295
3162
 
3296
3163
 
 
3164
def _fix_overwrite_type(overwrite):
 
3165
    if isinstance(overwrite, bool):
 
3166
        if overwrite:
 
3167
            return ["history", "tags"]
 
3168
        else:
 
3169
            return []
 
3170
    return overwrite
 
3171
 
 
3172
 
3297
3173
class GenericInterBranch(InterBranch):
3298
3174
    """InterBranch implementation that uses public Branch functions."""
3299
3175
 
3464
3340
        result.target_branch = self.target
3465
3341
        result.old_revno, result.old_revid = self.target.last_revision_info()
3466
3342
        self.source.update_references(self.target)
 
3343
        overwrite = _fix_overwrite_type(overwrite)
3467
3344
        if result.old_revid != stop_revision:
3468
3345
            # We assume that during 'push' this repository is closer than
3469
3346
            # the target.
3470
3347
            graph = self.source.repository.get_graph(self.target.repository)
3471
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3472
 
                    graph=graph)
 
3348
            self._update_revisions(stop_revision,
 
3349
                overwrite=("history" in overwrite),
 
3350
                graph=graph)
3473
3351
        if self.source._push_should_merge_tags():
3474
3352
            result.tag_updates, result.tag_conflicts = (
3475
 
                self.source.tags.merge_to(self.target.tags, overwrite))
 
3353
                self.source.tags.merge_to(
 
3354
                self.target.tags, "tags" in overwrite))
3476
3355
        result.new_revno, result.new_revid = self.target.last_revision_info()
3477
3356
        return result
3478
3357
 
3556
3435
            # -- JRV20090506
3557
3436
            result.old_revno, result.old_revid = \
3558
3437
                self.target.last_revision_info()
3559
 
            self._update_revisions(stop_revision, overwrite=overwrite,
 
3438
            overwrite = _fix_overwrite_type(overwrite)
 
3439
            self._update_revisions(stop_revision,
 
3440
                overwrite=("history" in overwrite),
3560
3441
                graph=graph)
3561
3442
            # TODO: The old revid should be specified when merging tags, 
3562
3443
            # so a tags implementation that versions tags can only 
3563
3444
            # pull in the most recent changes. -- JRV20090506
3564
3445
            result.tag_updates, result.tag_conflicts = (
3565
 
                self.source.tags.merge_to(self.target.tags, overwrite,
 
3446
                self.source.tags.merge_to(self.target.tags,
 
3447
                    "tags" in overwrite,
3566
3448
                    ignore_master=not merge_tags_to_master))
3567
3449
            result.new_revno, result.new_revid = self.target.last_revision_info()
3568
3450
            if _hook_master: