~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-03-06 06:48:25 UTC
  • mfrom: (4070.8.6 debug-config)
  • Revision ID: pqm@pqm.ubuntu.com-20090306064825-kbpwggw21dygeix6
(mbp) debug_flags configuration option

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008 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
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
17
 
18
18
import sys
30
30
        lockable_files,
31
31
        repository,
32
32
        revision as _mod_revision,
33
 
        symbol_versioning,
34
33
        transport,
35
34
        tsort,
36
35
        ui,
37
36
        urlutils,
38
37
        )
39
 
from bzrlib.config import BranchConfig, TransportConfig
 
38
from bzrlib.config import BranchConfig
40
39
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
41
40
from bzrlib.tag import (
42
41
    BasicTags,
45
44
""")
46
45
 
47
46
from bzrlib.decorators import needs_read_lock, needs_write_lock
48
 
from bzrlib.hooks import HookPoint, Hooks
 
47
from bzrlib.hooks import Hooks
49
48
from bzrlib.inter import InterObject
50
49
from bzrlib import registry
51
50
from bzrlib.symbol_versioning import (
84
83
    # - RBC 20060112
85
84
    base = None
86
85
 
 
86
    # override this to set the strategy for storing tags
 
87
    def _make_tags(self):
 
88
        return DisabledTags(self)
 
89
 
87
90
    def __init__(self, *ignored, **ignored_too):
88
 
        self.tags = self._format.make_tags(self)
 
91
        self.tags = self._make_tags()
89
92
        self._revision_history_cache = None
90
93
        self._revision_id_to_revno_cache = None
91
94
        self._partial_revision_id_to_revno_cache = {}
99
102
    def _open_hook(self):
100
103
        """Called by init to allow simpler extension of the base class."""
101
104
 
102
 
    def _activate_fallback_location(self, url):
103
 
        """Activate the branch/repository from url as a fallback repository."""
104
 
        self.repository.add_fallback_repository(
105
 
            self._get_fallback_repository(url))
106
 
 
107
105
    def break_lock(self):
108
106
        """Break a lock if one is present from another instance.
109
107
 
118
116
        if master is not None:
119
117
            master.break_lock()
120
118
 
121
 
    def _check_stackable_repo(self):
122
 
        if not self.repository._format.supports_external_lookups:
123
 
            raise errors.UnstackableRepositoryFormat(self.repository._format,
124
 
                self.repository.base)
125
 
 
126
119
    @staticmethod
127
120
    def open(base, _unsupported=False, possible_transports=None):
128
121
        """Open the branch rooted at base.
162
155
        The default implementation returns False if this branch has no tags,
163
156
        and True the rest of the time.  Subclasses may override this.
164
157
        """
165
 
        return self.supports_tags() and self.tags.get_tag_dict()
 
158
        return self.tags.supports_tags() and self.tags.get_tag_dict()
166
159
 
167
160
    def get_config(self):
168
161
        return BranchConfig(self)
169
162
 
170
 
    def _get_config(self):
171
 
        """Get the concrete config for just the config in this branch.
172
 
 
173
 
        This is not intended for client use; see Branch.get_config for the
174
 
        public API.
175
 
 
176
 
        Added in 1.14.
177
 
 
178
 
        :return: An object supporting get_option and set_option.
179
 
        """
180
 
        raise NotImplementedError(self._get_config)
181
 
 
182
 
    def _get_fallback_repository(self, url):
183
 
        """Get the repository we fallback to at url."""
184
 
        url = urlutils.join(self.base, url)
185
 
        a_bzrdir = bzrdir.BzrDir.open(url,
186
 
            possible_transports=[self.bzrdir.root_transport])
187
 
        return a_bzrdir.open_branch().repository
188
 
 
189
 
    def _get_tags_bytes(self):
190
 
        """Get the bytes of a serialised tags dict.
191
 
 
192
 
        Note that not all branches support tags, nor do all use the same tags
193
 
        logic: this method is specific to BasicTags. Other tag implementations
194
 
        may use the same method name and behave differently, safely, because
195
 
        of the double-dispatch via
196
 
        format.make_tags->tags_instance->get_tags_dict.
197
 
 
198
 
        :return: The bytes of the tags file.
199
 
        :seealso: Branch._set_tags_bytes.
200
 
        """
201
 
        return self._transport.get_bytes('tags')
202
 
 
203
163
    def _get_nick(self, local=False, possible_transports=None):
204
164
        config = self.get_config()
205
165
        # explicit overrides master, but don't look for master if local is True
508
468
        """
509
469
        if self.base == from_branch.base:
510
470
            return (0, [])
511
 
        if pb is not None:
512
 
            symbol_versioning.warn(
513
 
                symbol_versioning.deprecated_in((1, 14, 0))
514
 
                % "pb parameter to fetch()")
 
471
        if pb is None:
 
472
            nested_pb = ui.ui_factory.nested_progress_bar()
 
473
            pb = nested_pb
 
474
        else:
 
475
            nested_pb = None
 
476
 
515
477
        from_branch.lock_read()
516
478
        try:
517
479
            if last_revision is None:
 
480
                pb.update('get source history')
518
481
                last_revision = from_branch.last_revision()
519
482
                last_revision = _mod_revision.ensure_null(last_revision)
520
483
            return self.repository.fetch(from_branch.repository,
521
484
                                         revision_id=last_revision,
522
 
                                         pb=pb)
 
485
                                         pb=nested_pb)
523
486
        finally:
 
487
            if nested_pb is not None:
 
488
                nested_pb.finished()
524
489
            from_branch.unlock()
525
490
 
526
491
    def get_bound_location(self):
598
563
        :raises UnstackableRepositoryFormat: If the repository does not support
599
564
            stacking.
600
565
        """
601
 
        if not self._format.supports_stacking():
602
 
            raise errors.UnstackableBranchFormat(self._format, self.base)
603
 
        self._check_stackable_repo()
604
 
        if not url:
605
 
            try:
606
 
                old_url = self.get_stacked_on_url()
607
 
            except (errors.NotStacked, errors.UnstackableBranchFormat,
608
 
                errors.UnstackableRepositoryFormat):
609
 
                return
610
 
            url = ''
611
 
            # repositories don't offer an interface to remove fallback
612
 
            # repositories today; take the conceptually simpler option and just
613
 
            # reopen it.
614
 
            self.repository = self.bzrdir.find_repository()
615
 
            # for every revision reference the branch has, ensure it is pulled
616
 
            # in.
617
 
            source_repository = self._get_fallback_repository(old_url)
618
 
            for revision_id in chain([self.last_revision()],
619
 
                self.tags.get_reverse_tag_dict()):
620
 
                self.repository.fetch(source_repository, revision_id,
621
 
                    find_ghosts=True)
622
 
        else:
623
 
            self._activate_fallback_location(url)
624
 
        # write this out after the repository is stacked to avoid setting a
625
 
        # stacked config that doesn't work.
626
 
        self._set_config_location('stacked_on_location', url)
627
 
 
628
 
 
629
 
    def _set_tags_bytes(self, bytes):
630
 
        """Mirror method for _get_tags_bytes.
631
 
 
632
 
        :seealso: Branch._get_tags_bytes.
633
 
        """
634
 
        return _run_with_write_locked_target(self, self._transport.put_bytes,
635
 
            'tags', bytes)
 
566
        raise NotImplementedError(self.set_stacked_on_url)
636
567
 
637
568
    def _cache_revision_history(self, rev_history):
638
569
        """Set the cached revision history to rev_history.
892
823
    def get_public_branch(self):
893
824
        """Return the public location of the branch.
894
825
 
895
 
        This is used by merge directives.
 
826
        This is is used by merge directives.
896
827
        """
897
828
        return self._get_config_location('public_branch')
898
829
 
1349
1280
        """
1350
1281
        return True
1351
1282
 
1352
 
    def make_tags(self, branch):
1353
 
        """Create a tags object for branch.
1354
 
 
1355
 
        This method is on BranchFormat, because BranchFormats are reflected
1356
 
        over the wire via network_name(), whereas full Branch instances require
1357
 
        multiple VFS method calls to operate at all.
1358
 
 
1359
 
        The default implementation returns a disabled-tags instance.
1360
 
 
1361
 
        Note that it is normal for branch to be a RemoteBranch when using tags
1362
 
        on a RemoteBranch.
1363
 
        """
1364
 
        return DisabledTags(branch)
1365
 
 
1366
1283
    def network_name(self):
1367
1284
        """A simple byte string uniquely identifying this format for RPC calls.
1368
1285
 
1373
1290
        """
1374
1291
        raise NotImplementedError(self.network_name)
1375
1292
 
1376
 
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
 
1293
    def open(self, a_bzrdir, _found=False):
1377
1294
        """Return the branch object for a_bzrdir
1378
1295
 
1379
 
        :param a_bzrdir: A BzrDir that contains a branch.
1380
 
        :param _found: a private parameter, do not use it. It is used to
1381
 
            indicate if format probing has already be done.
1382
 
        :param ignore_fallbacks: when set, no fallback branches will be opened
1383
 
            (if there are any).  Default is to open fallbacks.
 
1296
        _found is a private parameter, do not use it. It is used to indicate
 
1297
               if format probing has already be done.
1384
1298
        """
1385
1299
        raise NotImplementedError(self.open)
1386
1300
 
1426
1340
        notified.
1427
1341
        """
1428
1342
        Hooks.__init__(self)
1429
 
        self.create_hook(HookPoint('set_rh',
1430
 
            "Invoked whenever the revision history has been set via "
1431
 
            "set_revision_history. The api signature is (branch, "
1432
 
            "revision_history), and the branch will be write-locked. "
1433
 
            "The set_rh hook can be expensive for bzr to trigger, a better "
1434
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1435
 
        self.create_hook(HookPoint('open',
1436
 
            "Called with the Branch object that has been opened after a "
1437
 
            "branch is opened.", (1, 8), None))
1438
 
        self.create_hook(HookPoint('post_push',
1439
 
            "Called after a push operation completes. post_push is called "
1440
 
            "with a bzrlib.branch.BranchPushResult object and only runs in the "
1441
 
            "bzr client.", (0, 15), None))
1442
 
        self.create_hook(HookPoint('post_pull',
1443
 
            "Called after a pull operation completes. post_pull is called "
1444
 
            "with a bzrlib.branch.PullResult object and only runs in the "
1445
 
            "bzr client.", (0, 15), None))
1446
 
        self.create_hook(HookPoint('pre_commit',
1447
 
            "Called after a commit is calculated but before it is is "
1448
 
            "completed. pre_commit is called with (local, master, old_revno, "
1449
 
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1450
 
            "). old_revid is NULL_REVISION for the first commit to a branch, "
1451
 
            "tree_delta is a TreeDelta object describing changes from the "
1452
 
            "basis revision. hooks MUST NOT modify this delta. "
1453
 
            " future_tree is an in-memory tree obtained from "
1454
 
            "CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1455
 
            "tree.", (0,91), None))
1456
 
        self.create_hook(HookPoint('post_commit',
1457
 
            "Called in the bzr client after a commit has completed. "
1458
 
            "post_commit is called with (local, master, old_revno, old_revid, "
1459
 
            "new_revno, new_revid). old_revid is NULL_REVISION for the first "
1460
 
            "commit to a branch.", (0, 15), None))
1461
 
        self.create_hook(HookPoint('post_uncommit',
1462
 
            "Called in the bzr client after an uncommit completes. "
1463
 
            "post_uncommit is called with (local, master, old_revno, "
1464
 
            "old_revid, new_revno, new_revid) where local is the local branch "
1465
 
            "or None, master is the target branch, and an empty branch "
1466
 
            "receives new_revno of 0, new_revid of None.", (0, 15), None))
1467
 
        self.create_hook(HookPoint('pre_change_branch_tip',
1468
 
            "Called in bzr client and server before a change to the tip of a "
1469
 
            "branch is made. pre_change_branch_tip is called with a "
1470
 
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1471
 
            "commit, uncommit will all trigger this hook.", (1, 6), None))
1472
 
        self.create_hook(HookPoint('post_change_branch_tip',
1473
 
            "Called in bzr client and server after a change to the tip of a "
1474
 
            "branch is made. post_change_branch_tip is called with a "
1475
 
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1476
 
            "commit, uncommit will all trigger this hook.", (1, 4), None))
1477
 
        self.create_hook(HookPoint('transform_fallback_location',
1478
 
            "Called when a stacked branch is activating its fallback "
1479
 
            "locations. transform_fallback_location is called with (branch, "
1480
 
            "url), and should return a new url. Returning the same url "
1481
 
            "allows it to be used as-is, returning a different one can be "
1482
 
            "used to cause the branch to stack on a closer copy of that "
1483
 
            "fallback_location. Note that the branch cannot have history "
1484
 
            "accessing methods called on it during this hook because the "
1485
 
            "fallback locations have not been activated. When there are "
1486
 
            "multiple hooks installed for transform_fallback_location, "
1487
 
            "all are called with the url returned from the previous hook."
1488
 
            "The order is however undefined.", (1, 9), None))
 
1343
        # Introduced in 0.15:
 
1344
        # invoked whenever the revision history has been set
 
1345
        # with set_revision_history. The api signature is
 
1346
        # (branch, revision_history), and the branch will
 
1347
        # be write-locked.
 
1348
        self['set_rh'] = []
 
1349
        # Invoked after a branch is opened. The api signature is (branch).
 
1350
        self['open'] = []
 
1351
        # invoked after a push operation completes.
 
1352
        # the api signature is
 
1353
        # (push_result)
 
1354
        # containing the members
 
1355
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
 
1356
        # where local is the local target branch or None, master is the target
 
1357
        # master branch, and the rest should be self explanatory. The source
 
1358
        # is read locked and the target branches write locked. Source will
 
1359
        # be the local low-latency branch.
 
1360
        self['post_push'] = []
 
1361
        # invoked after a pull operation completes.
 
1362
        # the api signature is
 
1363
        # (pull_result)
 
1364
        # containing the members
 
1365
        # (source, local, master, old_revno, old_revid, new_revno, new_revid)
 
1366
        # where local is the local branch or None, master is the target
 
1367
        # master branch, and the rest should be self explanatory. The source
 
1368
        # is read locked and the target branches write locked. The local
 
1369
        # branch is the low-latency branch.
 
1370
        self['post_pull'] = []
 
1371
        # invoked before a commit operation takes place.
 
1372
        # the api signature is
 
1373
        # (local, master, old_revno, old_revid, future_revno, future_revid,
 
1374
        #  tree_delta, future_tree).
 
1375
        # old_revid is NULL_REVISION for the first commit to a branch
 
1376
        # tree_delta is a TreeDelta object describing changes from the basis
 
1377
        # revision, hooks MUST NOT modify this delta
 
1378
        # future_tree is an in-memory tree obtained from
 
1379
        # CommitBuilder.revision_tree() and hooks MUST NOT modify this tree
 
1380
        self['pre_commit'] = []
 
1381
        # invoked after a commit operation completes.
 
1382
        # the api signature is
 
1383
        # (local, master, old_revno, old_revid, new_revno, new_revid)
 
1384
        # old_revid is NULL_REVISION for the first commit to a branch.
 
1385
        self['post_commit'] = []
 
1386
        # invoked after a uncommit operation completes.
 
1387
        # the api signature is
 
1388
        # (local, master, old_revno, old_revid, new_revno, new_revid) where
 
1389
        # local is the local branch or None, master is the target branch,
 
1390
        # and an empty branch recieves new_revno of 0, new_revid of None.
 
1391
        self['post_uncommit'] = []
 
1392
        # Introduced in 1.6
 
1393
        # Invoked before the tip of a branch changes.
 
1394
        # the api signature is
 
1395
        # (params) where params is a ChangeBranchTipParams with the members
 
1396
        # (branch, old_revno, new_revno, old_revid, new_revid)
 
1397
        self['pre_change_branch_tip'] = []
 
1398
        # Introduced in 1.4
 
1399
        # Invoked after the tip of a branch changes.
 
1400
        # the api signature is
 
1401
        # (params) where params is a ChangeBranchTipParams with the members
 
1402
        # (branch, old_revno, new_revno, old_revid, new_revid)
 
1403
        self['post_change_branch_tip'] = []
 
1404
        # Introduced in 1.9
 
1405
        # Invoked when a stacked branch activates its fallback locations and
 
1406
        # allows the transformation of the url of said location.
 
1407
        # the api signature is
 
1408
        # (branch, url) where branch is the branch having its fallback
 
1409
        # location activated and url is the url for the fallback location.
 
1410
        # The hook should return a url.
 
1411
        self['transform_fallback_location'] = []
1489
1412
 
1490
1413
 
1491
1414
# install the default hooks into the Branch class.
1558
1481
        """The network name for this format is the control dirs disk label."""
1559
1482
        return self._matchingbzrdir.get_format_string()
1560
1483
 
1561
 
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1562
 
        """See BranchFormat.open()."""
 
1484
    def open(self, a_bzrdir, _found=False):
 
1485
        """Return the branch object for a_bzrdir
 
1486
 
 
1487
        _found is a private parameter, do not use it. It is used to indicate
 
1488
               if format probing has already be done.
 
1489
        """
1563
1490
        if not _found:
1564
1491
            # we are being called directly and must probe.
1565
1492
            raise NotImplementedError
1586
1513
        """
1587
1514
        return self.get_format_string()
1588
1515
 
1589
 
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
1590
 
        """See BranchFormat.open()."""
 
1516
    def open(self, a_bzrdir, _found=False):
 
1517
        """Return the branch object for a_bzrdir.
 
1518
 
 
1519
        _found is a private parameter, do not use it. It is used to indicate
 
1520
               if format probing has already be done.
 
1521
        """
1591
1522
        if not _found:
1592
1523
            format = BranchFormat.find_format(a_bzrdir)
1593
1524
            if format.__class__ != self.__class__:
1600
1531
            return self._branch_class()(_format=self,
1601
1532
                              _control_files=control_files,
1602
1533
                              a_bzrdir=a_bzrdir,
1603
 
                              _repository=a_bzrdir.find_repository(),
1604
 
                              ignore_fallbacks=ignore_fallbacks)
 
1534
                              _repository=a_bzrdir.find_repository())
1605
1535
        except errors.NoSuchFile:
1606
1536
            raise errors.NotBranchError(path=transport.base)
1607
1537
 
1679
1609
                      ]
1680
1610
        return self._initialize_helper(a_bzrdir, utf8_files)
1681
1611
 
1682
 
    def make_tags(self, branch):
1683
 
        """See bzrlib.branch.BranchFormat.make_tags()."""
1684
 
        return BasicTags(branch)
1685
 
 
1686
 
 
1687
1612
 
1688
1613
class BzrBranchFormat7(BranchFormatMetadir):
1689
1614
    """Branch format with last-revision, tags, and a stacked location pointer.
1718
1643
        self._matchingbzrdir.repository_format = \
1719
1644
            RepositoryFormatKnitPack5RichRoot()
1720
1645
 
1721
 
    def make_tags(self, branch):
1722
 
        """See bzrlib.branch.BranchFormat.make_tags()."""
1723
 
        return BasicTags(branch)
1724
 
 
1725
1646
    def supports_stacking(self):
1726
1647
        return True
1727
1648
 
1787
1708
        return clone
1788
1709
 
1789
1710
    def open(self, a_bzrdir, _found=False, location=None,
1790
 
             possible_transports=None, ignore_fallbacks=False):
 
1711
             possible_transports=None):
1791
1712
        """Return the branch that the branch reference in a_bzrdir points at.
1792
1713
 
1793
 
        :param a_bzrdir: A BzrDir that contains a branch.
1794
 
        :param _found: a private parameter, do not use it. It is used to
1795
 
            indicate if format probing has already be done.
1796
 
        :param ignore_fallbacks: when set, no fallback branches will be opened
1797
 
            (if there are any).  Default is to open fallbacks.
1798
 
        :param location: The location of the referenced branch.  If
1799
 
            unspecified, this will be determined from the branch reference in
1800
 
            a_bzrdir.
1801
 
        :param possible_transports: An optional reusable transports list.
 
1714
        _found is a private parameter, do not use it. It is used to indicate
 
1715
               if format probing has already be done.
1802
1716
        """
1803
1717
        if not _found:
1804
1718
            format = BranchFormat.find_format(a_bzrdir)
1809
1723
            location = self.get_reference(a_bzrdir)
1810
1724
        real_bzrdir = bzrdir.BzrDir.open(
1811
1725
            location, possible_transports=possible_transports)
1812
 
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
 
1726
        result = real_bzrdir.open_branch()
1813
1727
        # this changes the behaviour of result.clone to create a new reference
1814
1728
        # rather than a copy of the content of the branch.
1815
1729
        # I did not use a proxy object because that needs much more extensive
1862
1776
    """
1863
1777
 
1864
1778
    def __init__(self, _format=None,
1865
 
                 _control_files=None, a_bzrdir=None, _repository=None,
1866
 
                 ignore_fallbacks=False):
 
1779
                 _control_files=None, a_bzrdir=None, _repository=None):
1867
1780
        """Create new branch object at a particular location."""
1868
1781
        if a_bzrdir is None:
1869
1782
            raise ValueError('a_bzrdir must be supplied')
1892
1805
 
1893
1806
    base = property(_get_base, doc="The URL for the root of this branch.")
1894
1807
 
1895
 
    def _get_config(self):
1896
 
        return TransportConfig(self._transport, 'branch.conf')
1897
 
 
1898
1808
    def is_locked(self):
1899
1809
        return self.control_files.is_locked()
1900
1810
 
2170
2080
 
2171
2081
        Must be called with self read locked and target write locked.
2172
2082
        """
2173
 
        result = BranchPushResult()
 
2083
        result = PushResult()
2174
2084
        result.source_branch = self
2175
2085
        result.target_branch = target
2176
2086
        result.old_revno, result.old_revid = target.last_revision_info()
2219
2129
            self._transport.put_bytes('parent', url + '\n',
2220
2130
                mode=self.bzrdir._get_file_mode())
2221
2131
 
 
2132
    def set_stacked_on_url(self, url):
 
2133
        raise errors.UnstackableBranchFormat(self._format, self.base)
 
2134
 
2222
2135
 
2223
2136
class BzrBranch5(BzrBranch):
2224
2137
    """A format 5 branch. This supports new features over plain branches.
2350
2263
class BzrBranch7(BzrBranch5):
2351
2264
    """A branch with support for a fallback repository."""
2352
2265
 
 
2266
    def _get_fallback_repository(self, url):
 
2267
        """Get the repository we fallback to at url."""
 
2268
        url = urlutils.join(self.base, url)
 
2269
        a_bzrdir = bzrdir.BzrDir.open(url,
 
2270
                                      possible_transports=[self._transport])
 
2271
        return a_bzrdir.open_branch().repository
 
2272
 
 
2273
    def _activate_fallback_location(self, url):
 
2274
        """Activate the branch/repository from url as a fallback repository."""
 
2275
        self.repository.add_fallback_repository(
 
2276
            self._get_fallback_repository(url))
 
2277
 
2353
2278
    def _open_hook(self):
2354
 
        if self._ignore_fallbacks:
2355
 
            return
2356
2279
        try:
2357
2280
            url = self.get_stacked_on_url()
2358
2281
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2368
2291
                        "None, not a URL." % hook_name)
2369
2292
            self._activate_fallback_location(url)
2370
2293
 
 
2294
    def _check_stackable_repo(self):
 
2295
        if not self.repository._format.supports_external_lookups:
 
2296
            raise errors.UnstackableRepositoryFormat(self.repository._format,
 
2297
                self.repository.base)
 
2298
 
2371
2299
    def __init__(self, *args, **kwargs):
2372
 
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2373
2300
        super(BzrBranch7, self).__init__(*args, **kwargs)
2374
2301
        self._last_revision_info_cache = None
2375
2302
        self._partial_revision_history_cache = []
2548
2475
        self.get_config().set_user_option('append_revisions_only', value,
2549
2476
            warn_masked=True)
2550
2477
 
 
2478
    def set_stacked_on_url(self, url):
 
2479
        self._check_stackable_repo()
 
2480
        if not url:
 
2481
            try:
 
2482
                old_url = self.get_stacked_on_url()
 
2483
            except (errors.NotStacked, errors.UnstackableBranchFormat,
 
2484
                errors.UnstackableRepositoryFormat):
 
2485
                return
 
2486
            url = ''
 
2487
            # repositories don't offer an interface to remove fallback
 
2488
            # repositories today; take the conceptually simpler option and just
 
2489
            # reopen it.
 
2490
            self.repository = self.bzrdir.find_repository()
 
2491
            # for every revision reference the branch has, ensure it is pulled
 
2492
            # in.
 
2493
            source_repository = self._get_fallback_repository(old_url)
 
2494
            for revision_id in chain([self.last_revision()],
 
2495
                self.tags.get_reverse_tag_dict()):
 
2496
                self.repository.fetch(source_repository, revision_id,
 
2497
                    find_ghosts=True)
 
2498
        else:
 
2499
            self._activate_fallback_location(url)
 
2500
        # write this out after the repository is stacked to avoid setting a
 
2501
        # stacked config that doesn't work.
 
2502
        self._set_config_location('stacked_on_location', url)
 
2503
 
2551
2504
    def _get_append_revisions_only(self):
2552
2505
        value = self.get_config().get_user_option('append_revisions_only')
2553
2506
        return value == 'True'
2554
2507
 
 
2508
    def _make_tags(self):
 
2509
        return BasicTags(self)
 
2510
 
2555
2511
    @needs_write_lock
2556
2512
    def generate_revision_history(self, revision_id, last_rev=None,
2557
2513
                                  other_branch=None):
2606
2562
    def get_stacked_on_url(self):
2607
2563
        raise errors.UnstackableBranchFormat(self._format, self.base)
2608
2564
 
 
2565
    def set_stacked_on_url(self, url):
 
2566
        raise errors.UnstackableBranchFormat(self._format, self.base)
 
2567
 
2609
2568
 
2610
2569
######################################################################
2611
2570
# results of operations
2628
2587
    :ivar new_revno: Revision number after pull.
2629
2588
    :ivar old_revid: Tip revision id before pull.
2630
2589
    :ivar new_revid: Tip revision id after pull.
2631
 
    :ivar source_branch: Source (local) branch object. (read locked)
 
2590
    :ivar source_branch: Source (local) branch object.
2632
2591
    :ivar master_branch: Master branch of the target, or the target if no
2633
2592
        Master
2634
2593
    :ivar local_branch: target branch if there is a Master, else None
2635
 
    :ivar target_branch: Target/destination branch object. (write locked)
 
2594
    :ivar target_branch: Target/destination branch object.
2636
2595
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
2637
2596
    """
2638
2597
 
2649
2608
        self._show_tag_conficts(to_file)
2650
2609
 
2651
2610
 
2652
 
class BranchPushResult(_Result):
 
2611
class PushResult(_Result):
2653
2612
    """Result of a Branch.push operation.
2654
2613
 
2655
 
    :ivar old_revno: Revision number (eg 10) of the target before push.
2656
 
    :ivar new_revno: Revision number (eg 12) of the target after push.
2657
 
    :ivar old_revid: Tip revision id (eg joe@foo.com-1234234-aoeua34) of target
2658
 
        before the push.
2659
 
    :ivar new_revid: Tip revision id (eg joe@foo.com-5676566-boa234a) of target
2660
 
        after the push.
2661
 
    :ivar source_branch: Source branch object that the push was from. This is
2662
 
        read locked, and generally is a local (and thus low latency) branch.
2663
 
    :ivar master_branch: If target is a bound branch, the master branch of
2664
 
        target, or target itself. Always write locked.
2665
 
    :ivar target_branch: The direct Branch where data is being sent (write
2666
 
        locked).
2667
 
    :ivar local_branch: If the target is a bound branch this will be the
2668
 
        target, otherwise it will be None.
 
2614
    :ivar old_revno: Revision number before push.
 
2615
    :ivar new_revno: Revision number after push.
 
2616
    :ivar old_revid: Tip revision id before push.
 
2617
    :ivar new_revid: Tip revision id after push.
 
2618
    :ivar source_branch: Source branch object.
 
2619
    :ivar master_branch: Master branch of the target, or None.
 
2620
    :ivar target_branch: Target/destination branch object.
2669
2621
    """
2670
2622
 
2671
2623
    def __int__(self):