~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

(vila) Revise legal option names to be less drastic. (Vincent Ladeuil)

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
 
        except errors.NoSuchFile:
1610
 
            raise errors.NotBranchError(path=name, bzrdir=controldir)
1611
 
        try:
1612
 
            format_string = transport.get_bytes("format")
1613
 
            return format_registry.get(format_string)
1614
 
        except errors.NoSuchFile:
1615
 
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1616
 
        except KeyError:
1617
 
            raise errors.UnknownFormatError(format=format_string, kind='branch')
1618
 
 
1619
 
    @classmethod
1620
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1621
 
    def get_default_format(klass):
1622
 
        """Return the current default format."""
1623
 
        return format_registry.get_default()
1624
 
 
1625
 
    @classmethod
1626
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1627
 
    def get_formats(klass):
1628
 
        """Get all the known formats.
1629
 
 
1630
 
        Warning: This triggers a load of all lazy registered formats: do not
1631
 
        use except when that is desireed.
1632
 
        """
1633
 
        return format_registry._get_all()
1634
 
 
1635
1598
    def get_reference(self, controldir, name=None):
1636
1599
        """Get the target reference of the branch in controldir.
1637
1600
 
1659
1622
        """
1660
1623
        raise NotImplementedError(self.set_reference)
1661
1624
 
1662
 
    def get_format_string(self):
1663
 
        """Return the ASCII format string that identifies this format."""
1664
 
        raise NotImplementedError(self.get_format_string)
1665
 
 
1666
1625
    def get_format_description(self):
1667
1626
        """Return the short format description for this format."""
1668
1627
        raise NotImplementedError(self.get_format_description)
1717
1676
        raise NotImplementedError(self.network_name)
1718
1677
 
1719
1678
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
1720
 
            found_repository=None):
 
1679
            found_repository=None, possible_transports=None):
1721
1680
        """Return the branch object for controldir.
1722
1681
 
1723
1682
        :param controldir: A ControlDir that contains a branch.
1729
1688
        """
1730
1689
        raise NotImplementedError(self.open)
1731
1690
 
1732
 
    @classmethod
1733
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1734
 
    def register_format(klass, format):
1735
 
        """Register a metadir format.
1736
 
 
1737
 
        See MetaDirBranchFormatFactory for the ability to register a format
1738
 
        without loading the code the format needs until it is actually used.
1739
 
        """
1740
 
        format_registry.register(format)
1741
 
 
1742
 
    @classmethod
1743
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1744
 
    def set_default_format(klass, format):
1745
 
        format_registry.set_default(format)
1746
 
 
1747
1691
    def supports_set_append_revisions_only(self):
1748
1692
        """True if this format supports set_append_revisions_only."""
1749
1693
        return False
1756
1700
        """True if this format supports leaving locks in place."""
1757
1701
        return False # by default
1758
1702
 
1759
 
    @classmethod
1760
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1761
 
    def unregister_format(klass, format):
1762
 
        format_registry.remove(format)
1763
 
 
1764
1703
    def __str__(self):
1765
1704
        return self.get_format_description().rstrip()
1766
1705
 
1795
1734
        """
1796
1735
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
1797
1736
        self._format_string = format_string
1798
 
        
 
1737
 
1799
1738
    def get_format_string(self):
1800
1739
        """See BranchFormat.get_format_string."""
1801
1740
        return self._format_string
1808
1747
class BranchHooks(Hooks):
1809
1748
    """A dictionary mapping hook name to a list of callables for branch hooks.
1810
1749
 
1811
 
    e.g. ['set_rh'] Is the list of items to be called when the
1812
 
    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.
1813
1752
    """
1814
1753
 
1815
1754
    def __init__(self):
1819
1758
        notified.
1820
1759
        """
1821
1760
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1822
 
        self.add_hook('set_rh',
1823
 
            "Invoked whenever the revision history has been set via "
1824
 
            "set_revision_history. The api signature is (branch, "
1825
 
            "revision_history), and the branch will be write-locked. "
1826
 
            "The set_rh hook can be expensive for bzr to trigger, a better "
1827
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
1828
1761
        self.add_hook('open',
1829
1762
            "Called with the Branch object that has been opened after a "
1830
1763
            "branch is opened.", (1, 8))
2013
1946
            self.revision_id)
2014
1947
 
2015
1948
 
2016
 
class BranchFormatMetadir(BranchFormat):
2017
 
    """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)
2018
1969
 
2019
1970
    def _branch_class(self):
2020
1971
        """What class to instantiate on open calls."""
2030
1981
 
2031
1982
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
2032
1983
                           repository=None):
2033
 
        """Initialize a branch in a bzrdir, with specified files
 
1984
        """Initialize a branch in a control dir, with specified files
2034
1985
 
2035
1986
        :param a_bzrdir: The bzrdir to initialize the branch in
2036
1987
        :param utf8_files: The files to create as a list of
2038
1989
        :param name: Name of colocated branch to create, if any
2039
1990
        :return: a branch in this format
2040
1991
        """
 
1992
        if name is None:
 
1993
            name = a_bzrdir._get_selected_branch()
2041
1994
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
2042
1995
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2043
1996
        control_files = lockable_files.LockableFiles(branch_transport,
2045
1998
        control_files.create_lock()
2046
1999
        control_files.lock_write()
2047
2000
        try:
2048
 
            utf8_files += [('format', self.get_format_string())]
 
2001
            utf8_files += [('format', self.as_string())]
2049
2002
            for (filename, content) in utf8_files:
2050
2003
                branch_transport.put_bytes(
2051
2004
                    filename, content,
2057
2010
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2058
2011
        return branch
2059
2012
 
2060
 
    def network_name(self):
2061
 
        """A simple byte string uniquely identifying this format for RPC calls.
2062
 
 
2063
 
        Metadir branch formats use their format string.
2064
 
        """
2065
 
        return self.get_format_string()
2066
 
 
2067
2013
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2068
 
            found_repository=None):
 
2014
            found_repository=None, possible_transports=None):
2069
2015
        """See BranchFormat.open()."""
 
2016
        if name is None:
 
2017
            name = a_bzrdir._get_selected_branch()
2070
2018
        if not _found:
2071
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2019
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2072
2020
            if format.__class__ != self.__class__:
2073
2021
                raise AssertionError("wrong format %r found for %r" %
2074
2022
                    (format, self))
2083
2031
                              name=name,
2084
2032
                              a_bzrdir=a_bzrdir,
2085
2033
                              _repository=found_repository,
2086
 
                              ignore_fallbacks=ignore_fallbacks)
 
2034
                              ignore_fallbacks=ignore_fallbacks,
 
2035
                              possible_transports=possible_transports)
2087
2036
        except errors.NoSuchFile:
2088
2037
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2089
2038
 
2099
2048
    def supports_leaving_lock(self):
2100
2049
        return True
2101
2050
 
2102
 
 
2103
 
class BzrBranchFormat5(BranchFormatMetadir):
2104
 
    """Bzr branch format 5.
2105
 
 
2106
 
    This format has:
2107
 
     - a revision-history file.
2108
 
     - a format string
2109
 
     - a lock dir guarding the branch itself
2110
 
     - all of this stored in a branch/ subdirectory
2111
 
     - works with shared repositories.
2112
 
 
2113
 
    This format is new in bzr 0.8.
2114
 
    """
2115
 
 
2116
 
    def _branch_class(self):
2117
 
        return BzrBranch5
2118
 
 
2119
 
    def get_format_string(self):
2120
 
        """See BranchFormat.get_format_string()."""
2121
 
        return "Bazaar-NG branch format 5\n"
2122
 
 
2123
 
    def get_format_description(self):
2124
 
        """See BranchFormat.get_format_description()."""
2125
 
        return "Branch format 5"
2126
 
 
2127
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2128
 
                   append_revisions_only=None):
2129
 
        """Create a branch of this format in a_bzrdir."""
2130
 
        if append_revisions_only:
2131
 
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2132
 
        utf8_files = [('revision-history', ''),
2133
 
                      ('branch-name', ''),
2134
 
                      ]
2135
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2136
 
 
2137
 
    def supports_tags(self):
2138
 
        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)
2139
2058
 
2140
2059
 
2141
2060
class BzrBranchFormat6(BranchFormatMetadir):
2152
2071
    def _branch_class(self):
2153
2072
        return BzrBranch6
2154
2073
 
2155
 
    def get_format_string(self):
 
2074
    @classmethod
 
2075
    def get_format_string(cls):
2156
2076
        """See BranchFormat.get_format_string()."""
2157
2077
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
2158
2078
 
2184
2104
    def _branch_class(self):
2185
2105
        return BzrBranch8
2186
2106
 
2187
 
    def get_format_string(self):
 
2107
    @classmethod
 
2108
    def get_format_string(cls):
2188
2109
        """See BranchFormat.get_format_string()."""
2189
2110
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2190
2111
 
2238
2159
    def _branch_class(self):
2239
2160
        return BzrBranch7
2240
2161
 
2241
 
    def get_format_string(self):
 
2162
    @classmethod
 
2163
    def get_format_string(cls):
2242
2164
        """See BranchFormat.get_format_string()."""
2243
2165
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2244
2166
 
2270
2192
     - a format string
2271
2193
    """
2272
2194
 
2273
 
    def get_format_string(self):
 
2195
    @classmethod
 
2196
    def get_format_string(cls):
2274
2197
        """See BranchFormat.get_format_string()."""
2275
2198
        return "Bazaar-NG Branch Reference Format 1\n"
2276
2199
 
2298
2221
        mutter('creating branch reference in %s', a_bzrdir.user_url)
2299
2222
        if a_bzrdir._format.fixed_components:
2300
2223
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
 
2224
        if name is None:
 
2225
            name = a_bzrdir._get_selected_branch()
2301
2226
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2302
2227
        branch_transport.put_bytes('location',
2303
 
            target_branch.bzrdir.user_url)
2304
 
        branch_transport.put_bytes('format', self.get_format_string())
2305
 
        branch = self.open(
2306
 
            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,
2307
2231
            possible_transports=[target_branch.bzrdir.root_transport])
2308
2232
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2309
2233
        return branch
2335
2259
            a_bzrdir.
2336
2260
        :param possible_transports: An optional reusable transports list.
2337
2261
        """
 
2262
        if name is None:
 
2263
            name = a_bzrdir._get_selected_branch()
2338
2264
        if not _found:
2339
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2265
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2340
2266
            if format.__class__ != self.__class__:
2341
2267
                raise AssertionError("wrong format %r found for %r" %
2342
2268
                    (format, self))
2344
2270
            location = self.get_reference(a_bzrdir, name)
2345
2271
        real_bzrdir = controldir.ControlDir.open(
2346
2272
            location, possible_transports=possible_transports)
2347
 
        result = real_bzrdir.open_branch(name=name, 
2348
 
            ignore_fallbacks=ignore_fallbacks)
 
2273
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
 
2274
            possible_transports=possible_transports)
2349
2275
        # this changes the behaviour of result.clone to create a new reference
2350
2276
        # rather than a copy of the content of the branch.
2351
2277
        # I did not use a proxy object because that needs much more extensive
2385
2311
 
2386
2312
# formats which have no format string are not discoverable
2387
2313
# and not independently creatable, so are not registered.
2388
 
__format5 = BzrBranchFormat5()
2389
2314
__format6 = BzrBranchFormat6()
2390
2315
__format7 = BzrBranchFormat7()
2391
2316
__format8 = BzrBranchFormat8()
2392
 
format_registry.register(__format5)
 
2317
format_registry.register_lazy(
 
2318
    "Bazaar-NG branch format 5\n", "bzrlib.branchfmt.fullhistory", "BzrBranchFormat5")
2393
2319
format_registry.register(BranchReferenceFormat())
2394
2320
format_registry.register(__format6)
2395
2321
format_registry.register(__format7)
2432
2358
 
2433
2359
    def __init__(self, _format=None,
2434
2360
                 _control_files=None, a_bzrdir=None, name=None,
2435
 
                 _repository=None, ignore_fallbacks=False):
 
2361
                 _repository=None, ignore_fallbacks=False,
 
2362
                 possible_transports=None):
2436
2363
        """Create new branch object at a particular location."""
2437
2364
        if a_bzrdir is None:
2438
2365
            raise ValueError('a_bzrdir must be supplied')
2439
 
        else:
2440
 
            self.bzrdir = a_bzrdir
2441
 
        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
2442
2374
        self.name = name
2443
 
        # XXX: We should be able to just do
2444
 
        #   self.base = self.bzrdir.root_transport.base
2445
 
        # but this does not quite work yet -- mbp 20080522
2446
2375
        self._format = _format
2447
2376
        if _control_files is None:
2448
2377
            raise ValueError('BzrBranch _control_files is None')
2449
2378
        self.control_files = _control_files
2450
2379
        self._transport = _control_files._transport
2451
2380
        self.repository = _repository
2452
 
        Branch.__init__(self)
 
2381
        self.conf_store = None
 
2382
        Branch.__init__(self, possible_transports)
2453
2383
 
2454
2384
    def __str__(self):
2455
 
        if self.name is None:
2456
 
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
2457
 
        else:
2458
 
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2459
 
                self.name)
 
2385
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
2460
2386
 
2461
2387
    __repr__ = __str__
2462
2388
 
2466
2392
 
2467
2393
    base = property(_get_base, doc="The URL for the root of this branch.")
2468
2394
 
 
2395
    @property
 
2396
    def user_transport(self):
 
2397
        return self._user_transport
 
2398
 
2469
2399
    def _get_config(self):
2470
2400
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
2471
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
 
2472
2446
    def is_locked(self):
2473
2447
        return self.control_files.is_locked()
2474
2448
 
2481
2455
        """
2482
2456
        if not self.is_locked():
2483
2457
            self._note_lock('w')
2484
 
        # All-in-one needs to always unlock/lock.
2485
 
        repo_control = getattr(self.repository, 'control_files', None)
2486
 
        if self.control_files == repo_control or not self.is_locked():
2487
2458
            self.repository._warn_if_deprecated(self)
2488
2459
            self.repository.lock_write()
2489
2460
            took_lock = True
2504
2475
        """
2505
2476
        if not self.is_locked():
2506
2477
            self._note_lock('r')
2507
 
        # All-in-one needs to always unlock/lock.
2508
 
        repo_control = getattr(self.repository, 'control_files', None)
2509
 
        if self.control_files == repo_control or not self.is_locked():
2510
2478
            self.repository._warn_if_deprecated(self)
2511
2479
            self.repository.lock_read()
2512
2480
            took_lock = True
2522
2490
 
2523
2491
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2524
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()
2525
2495
        try:
2526
2496
            self.control_files.unlock()
2527
2497
        finally:
2528
 
            # All-in-one needs to always unlock/lock.
2529
 
            repo_control = getattr(self.repository, 'control_files', None)
2530
 
            if (self.control_files == repo_control or
2531
 
                not self.control_files.is_locked()):
2532
 
                self.repository.unlock()
2533
2498
            if not self.control_files.is_locked():
 
2499
                self.repository.unlock()
2534
2500
                # we just released the lock
2535
2501
                self._clear_cached_state()
2536
2502
 
2703
2669
        self._transport.put_bytes('last-revision', out_string,
2704
2670
            mode=self.bzrdir._get_file_mode())
2705
2671
 
2706
 
 
2707
 
class FullHistoryBzrBranch(BzrBranch):
2708
 
    """Bzr branch which contains the full revision history."""
2709
 
 
2710
 
    @needs_write_lock
2711
 
    def set_last_revision_info(self, revno, revision_id):
2712
 
        if not revision_id or not isinstance(revision_id, basestring):
2713
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2714
 
        revision_id = _mod_revision.ensure_null(revision_id)
2715
 
        # this old format stores the full history, but this api doesn't
2716
 
        # provide it, so we must generate, and might as well check it's
2717
 
        # correct
2718
 
        history = self._lefthand_history(revision_id)
2719
 
        if len(history) != revno:
2720
 
            raise AssertionError('%d != %d' % (len(history), revno))
2721
 
        self._set_revision_history(history)
2722
 
 
2723
 
    def _read_last_revision_info(self):
2724
 
        rh = self._revision_history()
2725
 
        revno = len(rh)
2726
 
        if revno:
2727
 
            return (revno, rh[-1])
2728
 
        else:
2729
 
            return (0, _mod_revision.NULL_REVISION)
2730
 
 
2731
 
    @deprecated_method(deprecated_in((2, 4, 0)))
2732
 
    @needs_write_lock
2733
 
    def set_revision_history(self, rev_history):
2734
 
        """See Branch.set_revision_history."""
2735
 
        self._set_revision_history(rev_history)
2736
 
 
2737
 
    def _set_revision_history(self, rev_history):
2738
 
        if 'evil' in debug.debug_flags:
2739
 
            mutter_callsite(3, "set_revision_history scales with history.")
2740
 
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2741
 
        for rev_id in rev_history:
2742
 
            check_not_reserved_id(rev_id)
2743
 
        if Branch.hooks['post_change_branch_tip']:
2744
 
            # Don't calculate the last_revision_info() if there are no hooks
2745
 
            # that will use it.
2746
 
            old_revno, old_revid = self.last_revision_info()
2747
 
        if len(rev_history) == 0:
2748
 
            revid = _mod_revision.NULL_REVISION
2749
 
        else:
2750
 
            revid = rev_history[-1]
2751
 
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2752
 
        self._write_revision_history(rev_history)
2753
 
        self._clear_cached_state()
2754
 
        self._cache_revision_history(rev_history)
2755
 
        for hook in Branch.hooks['set_rh']:
2756
 
            hook(self, rev_history)
2757
 
        if Branch.hooks['post_change_branch_tip']:
2758
 
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2759
 
 
2760
 
    def _write_revision_history(self, history):
2761
 
        """Factored out of set_revision_history.
2762
 
 
2763
 
        This performs the actual writing to disk.
2764
 
        It is intended to be called by set_revision_history."""
2765
 
        self._transport.put_bytes(
2766
 
            'revision-history', '\n'.join(history),
2767
 
            mode=self.bzrdir._get_file_mode())
2768
 
 
2769
 
    def _gen_revision_history(self):
2770
 
        history = self._transport.get_bytes('revision-history').split('\n')
2771
 
        if history[-1:] == ['']:
2772
 
            # There shouldn't be a trailing newline, but just in case.
2773
 
            history.pop()
2774
 
        return history
2775
 
 
2776
 
    def _synchronize_history(self, destination, revision_id):
2777
 
        if not isinstance(destination, FullHistoryBzrBranch):
2778
 
            super(BzrBranch, self)._synchronize_history(
2779
 
                destination, revision_id)
2780
 
            return
2781
 
        if revision_id == _mod_revision.NULL_REVISION:
2782
 
            new_history = []
2783
 
        else:
2784
 
            new_history = self._revision_history()
2785
 
        if revision_id is not None and new_history != []:
2786
 
            try:
2787
 
                new_history = new_history[:new_history.index(revision_id) + 1]
2788
 
            except ValueError:
2789
 
                rev = self.repository.get_revision(revision_id)
2790
 
                new_history = rev.get_history(self.repository)[1:]
2791
 
        destination._set_revision_history(new_history)
2792
 
 
2793
 
    @needs_write_lock
2794
 
    def generate_revision_history(self, revision_id, last_rev=None,
2795
 
        other_branch=None):
2796
 
        """Create a new revision history that will finish with revision_id.
2797
 
 
2798
 
        :param revision_id: the new tip to use.
2799
 
        :param last_rev: The previous last_revision. If not None, then this
2800
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
2801
 
        :param other_branch: The other branch that DivergedBranches should
2802
 
            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
2803
2678
        """
2804
 
        self._set_revision_history(self._lefthand_history(revision_id,
2805
 
            last_rev, other_branch))
2806
 
 
2807
 
 
2808
 
class BzrBranch5(FullHistoryBzrBranch):
2809
 
    """A format 5 branch. This supports new features over plain branches.
2810
 
 
2811
 
    It has support for a master_branch which is the data for bound branches.
2812
 
    """
 
2679
        self._format._update_feature_flags(updated_flags)
 
2680
        self.control_transport.put_bytes('format', self._format.as_string())
2813
2681
 
2814
2682
 
2815
2683
class BzrBranch8(BzrBranch):
2816
2684
    """A branch that stores tree-reference locations."""
2817
2685
 
2818
 
    def _open_hook(self):
 
2686
    def _open_hook(self, possible_transports=None):
2819
2687
        if self._ignore_fallbacks:
2820
2688
            return
 
2689
        if possible_transports is None:
 
2690
            possible_transports = [self.bzrdir.root_transport]
2821
2691
        try:
2822
2692
            url = self.get_stacked_on_url()
2823
2693
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2831
2701
                    raise AssertionError(
2832
2702
                        "'transform_fallback_location' hook %s returned "
2833
2703
                        "None, not a URL." % hook_name)
2834
 
            self._activate_fallback_location(url)
 
2704
            self._activate_fallback_location(url,
 
2705
                possible_transports=possible_transports)
2835
2706
 
2836
2707
    def __init__(self, *args, **kwargs):
2837
2708
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2955
2826
        """See Branch.set_push_location."""
2956
2827
        self._master_branch_cache = None
2957
2828
        result = None
2958
 
        config = self.get_config()
 
2829
        conf = self.get_config_stack()
2959
2830
        if location is None:
2960
 
            if config.get_user_option('bound') != 'True':
 
2831
            if not conf.get('bound'):
2961
2832
                return False
2962
2833
            else:
2963
 
                config.set_user_option('bound', 'False', warn_masked=True)
 
2834
                conf.set('bound', 'False')
2964
2835
                return True
2965
2836
        else:
2966
2837
            self._set_config_location('bound_location', location,
2967
 
                                      config=config)
2968
 
            config.set_user_option('bound', 'True', warn_masked=True)
 
2838
                                      config=conf)
 
2839
            conf.set('bound', 'True')
2969
2840
        return True
2970
2841
 
2971
2842
    def _get_bound_location(self, bound):
2972
2843
        """Return the bound location in the config file.
2973
2844
 
2974
2845
        Return None if the bound parameter does not match"""
2975
 
        config = self.get_config()
2976
 
        config_bound = (config.get_user_option('bound') == 'True')
2977
 
        if config_bound != bound:
 
2846
        conf = self.get_config_stack()
 
2847
        if conf.get('bound') != bound:
2978
2848
            return None
2979
 
        return self._get_config_location('bound_location', config=config)
 
2849
        return self._get_config_location('bound_location', config=conf)
2980
2850
 
2981
2851
    def get_bound_location(self):
2982
 
        """See Branch.set_push_location."""
 
2852
        """See Branch.get_bound_location."""
2983
2853
        return self._get_bound_location(True)
2984
2854
 
2985
2855
    def get_old_bound_location(self):
2992
2862
        ## self._check_stackable_repo()
2993
2863
        # stacked_on_location is only ever defined in branch.conf, so don't
2994
2864
        # waste effort reading the whole stack of config files.
2995
 
        config = self.get_config()._get_branch_data_config()
 
2865
        conf = _mod_config.BranchOnlyStack(self)
2996
2866
        stacked_url = self._get_config_location('stacked_on_location',
2997
 
            config=config)
 
2867
                                                config=conf)
2998
2868
        if stacked_url is None:
2999
2869
            raise errors.NotStacked(self)
3000
 
        return stacked_url
 
2870
        return stacked_url.encode('utf-8')
3001
2871
 
3002
2872
    @needs_read_lock
3003
2873
    def get_rev_id(self, revno, history=None):
3095
2965
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3096
2966
    """
3097
2967
 
3098
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3099
 
    def __int__(self):
3100
 
        """Return the relative change in revno.
3101
 
 
3102
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3103
 
        """
3104
 
        return self.new_revno - self.old_revno
3105
 
 
3106
2968
    def report(self, to_file):
3107
2969
        tag_conflicts = getattr(self, "tag_conflicts", None)
3108
2970
        tag_updates = getattr(self, "tag_updates", None)
3138
3000
        target, otherwise it will be None.
3139
3001
    """
3140
3002
 
3141
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3142
 
    def __int__(self):
3143
 
        """Return the relative change in revno.
3144
 
 
3145
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3146
 
        """
3147
 
        return self.new_revno - self.old_revno
3148
 
 
3149
3003
    def report(self, to_file):
3150
3004
        # TODO: This function gets passed a to_file, but then
3151
3005
        # ignores it and calls note() instead. This is also
3198
3052
 
3199
3053
        # Copy source data into target
3200
3054
        new_branch._write_last_revision_info(*branch.last_revision_info())
3201
 
        new_branch.set_parent(branch.get_parent())
3202
 
        new_branch.set_bound_location(branch.get_bound_location())
3203
 
        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()
3204
3062
 
3205
3063
        # New branch has no tags by default
3206
3064
        new_branch.tags._set_tag_dict({})
3207
3065
 
3208
3066
        # Copying done; now update target format
3209
3067
        new_branch._transport.put_bytes('format',
3210
 
            format.get_format_string(),
 
3068
            format.as_string(),
3211
3069
            mode=new_branch.bzrdir._get_file_mode())
3212
3070
 
3213
3071
        # Clean up old files
3214
3072
        new_branch._transport.delete('revision-history')
 
3073
        branch.lock_write()
3215
3074
        try:
3216
 
            branch.set_parent(None)
3217
 
        except errors.NoSuchFile:
3218
 
            pass
3219
 
        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()
3220
3082
 
3221
3083
 
3222
3084
class Converter6to7(object):
3226
3088
        format = BzrBranchFormat7()
3227
3089
        branch._set_config_location('stacked_on_location', '')
3228
3090
        # update target format
3229
 
        branch._transport.put_bytes('format', format.get_format_string())
 
3091
        branch._transport.put_bytes('format', format.as_string())
3230
3092
 
3231
3093
 
3232
3094
class Converter7to8(object):
3236
3098
        format = BzrBranchFormat8()
3237
3099
        branch._transport.put_bytes('references', '')
3238
3100
        # update target format
3239
 
        branch._transport.put_bytes('format', format.get_format_string())
 
3101
        branch._transport.put_bytes('format', format.as_string())
3240
3102
 
3241
3103
 
3242
3104
class InterBranch(InterObject):
3299
3161
        raise NotImplementedError(self.fetch)
3300
3162
 
3301
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
 
3302
3173
class GenericInterBranch(InterBranch):
3303
3174
    """InterBranch implementation that uses public Branch functions."""
3304
3175
 
3469
3340
        result.target_branch = self.target
3470
3341
        result.old_revno, result.old_revid = self.target.last_revision_info()
3471
3342
        self.source.update_references(self.target)
 
3343
        overwrite = _fix_overwrite_type(overwrite)
3472
3344
        if result.old_revid != stop_revision:
3473
3345
            # We assume that during 'push' this repository is closer than
3474
3346
            # the target.
3475
3347
            graph = self.source.repository.get_graph(self.target.repository)
3476
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3477
 
                    graph=graph)
 
3348
            self._update_revisions(stop_revision,
 
3349
                overwrite=("history" in overwrite),
 
3350
                graph=graph)
3478
3351
        if self.source._push_should_merge_tags():
3479
3352
            result.tag_updates, result.tag_conflicts = (
3480
 
                self.source.tags.merge_to(self.target.tags, overwrite))
 
3353
                self.source.tags.merge_to(
 
3354
                self.target.tags, "tags" in overwrite))
3481
3355
        result.new_revno, result.new_revid = self.target.last_revision_info()
3482
3356
        return result
3483
3357
 
3561
3435
            # -- JRV20090506
3562
3436
            result.old_revno, result.old_revid = \
3563
3437
                self.target.last_revision_info()
3564
 
            self._update_revisions(stop_revision, overwrite=overwrite,
 
3438
            overwrite = _fix_overwrite_type(overwrite)
 
3439
            self._update_revisions(stop_revision,
 
3440
                overwrite=("history" in overwrite),
3565
3441
                graph=graph)
3566
3442
            # TODO: The old revid should be specified when merging tags, 
3567
3443
            # so a tags implementation that versions tags can only 
3568
3444
            # pull in the most recent changes. -- JRV20090506
3569
3445
            result.tag_updates, result.tag_conflicts = (
3570
 
                self.source.tags.merge_to(self.target.tags, overwrite,
 
3446
                self.source.tags.merge_to(self.target.tags,
 
3447
                    "tags" in overwrite,
3571
3448
                    ignore_master=not merge_tags_to_master))
3572
3449
            result.new_revno, result.new_revid = self.target.last_revision_info()
3573
3450
            if _hook_master: