~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

Merge bzr.dev into cleanup resolving conflicts

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
        bzrdir,
26
26
        cache_utf8,
27
27
        config as _mod_config,
 
28
        controldir,
28
29
        debug,
29
30
        errors,
30
31
        lockdir,
31
32
        lockable_files,
 
33
        remote,
32
34
        repository,
33
35
        revision as _mod_revision,
34
36
        rio,
49
51
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
50
52
from bzrlib.hooks import HookPoint, Hooks
51
53
from bzrlib.inter import InterObject
52
 
from bzrlib.lock import _RelockDebugMixin
 
54
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
53
55
from bzrlib import registry
54
56
from bzrlib.symbol_versioning import (
55
57
    deprecated_in,
63
65
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
64
66
 
65
67
 
66
 
# TODO: Maybe include checks for common corruption of newlines, etc?
67
 
 
68
 
# TODO: Some operations like log might retrieve the same revisions
69
 
# repeatedly to calculate deltas.  We could perhaps have a weakref
70
 
# cache in memory to make this faster.  In general anything can be
71
 
# cached in memory between lock and unlock operations. .. nb thats
72
 
# what the transaction identity map provides
73
 
 
74
 
 
75
 
######################################################################
76
 
# branch objects
77
 
 
78
 
class Branch(object):
 
68
class Branch(controldir.ControlComponent):
79
69
    """Branch holding a history of revisions.
80
70
 
81
 
    base
82
 
        Base directory/url of the branch.
 
71
    :ivar base:
 
72
        Base directory/url of the branch; using control_url and
 
73
        control_transport is more standardized.
83
74
 
84
75
    hooks: An instance of BranchHooks.
85
76
    """
87
78
    # - RBC 20060112
88
79
    base = None
89
80
 
 
81
    @property
 
82
    def control_transport(self):
 
83
        return self._transport
 
84
 
 
85
    @property
 
86
    def user_transport(self):
 
87
        return self.bzrdir.user_transport
 
88
 
90
89
    def __init__(self, *ignored, **ignored_too):
91
90
        self.tags = self._format.make_tags(self)
92
91
        self._revision_history_cache = None
107
106
        """Activate the branch/repository from url as a fallback repository."""
108
107
        repo = self._get_fallback_repository(url)
109
108
        if repo.has_same_location(self.repository):
110
 
            raise errors.UnstackableLocationError(self.base, url)
 
109
            raise errors.UnstackableLocationError(self.user_url, url)
111
110
        self.repository.add_fallback_repository(repo)
112
111
 
113
112
    def break_lock(self):
200
199
        return self.supports_tags() and self.tags.get_tag_dict()
201
200
 
202
201
    def get_config(self):
 
202
        """Get a bzrlib.config.BranchConfig for this Branch.
 
203
 
 
204
        This can then be used to get and set configuration options for the
 
205
        branch.
 
206
 
 
207
        :return: A bzrlib.config.BranchConfig.
 
208
        """
203
209
        return BranchConfig(self)
204
210
 
205
211
    def _get_config(self):
241
247
        if not local and not config.has_explicit_nickname():
242
248
            try:
243
249
                master = self.get_master_branch(possible_transports)
 
250
                if master and self.user_url == master.user_url:
 
251
                    raise errors.RecursiveBind(self.user_url)
244
252
                if master is not None:
245
253
                    # return the master branch value
246
254
                    return master.nick
 
255
            except errors.RecursiveBind, e:
 
256
                raise e
247
257
            except errors.BzrError, e:
248
258
                # Silently fall back to local implicit nick if the master is
249
259
                # unavailable
286
296
        new_history.reverse()
287
297
        return new_history
288
298
 
289
 
    def lock_write(self):
 
299
    def lock_write(self, token=None):
 
300
        """Lock the branch for write operations.
 
301
 
 
302
        :param token: A token to permit reacquiring a previously held and
 
303
            preserved lock.
 
304
        :return: A BranchWriteLockResult.
 
305
        """
290
306
        raise NotImplementedError(self.lock_write)
291
307
 
292
308
    def lock_read(self):
 
309
        """Lock the branch for read operations.
 
310
 
 
311
        :return: A bzrlib.lock.LogicalLockResult.
 
312
        """
293
313
        raise NotImplementedError(self.lock_read)
294
314
 
295
315
    def unlock(self):
420
440
            * 'include' - the stop revision is the last item in the result
421
441
            * 'with-merges' - include the stop revision and all of its
422
442
              merged revisions in the result
 
443
            * 'with-merges-without-common-ancestry' - filter out revisions 
 
444
              that are in both ancestries
423
445
        :param direction: either 'reverse' or 'forward':
424
446
            * reverse means return the start_revision_id first, i.e.
425
447
              start at the most recent revision and go backwards in history
456
478
            stop_revision_id, stop_rule)
457
479
        # Make sure we don't return revisions that are not part of the
458
480
        # start_revision_id ancestry.
459
 
        filtered = self._filter_non_ancestors(filtered)
 
481
        filtered = self._filter_start_non_ancestors(filtered)
460
482
        if direction == 'reverse':
461
483
            return filtered
462
484
        if direction == 'forward':
499
521
                       node.end_of_merge)
500
522
                if rev_id == stop_revision_id:
501
523
                    return
 
524
        elif stop_rule == 'with-merges-without-common-ancestry':
 
525
            # We want to exclude all revisions that are already part of the
 
526
            # stop_revision_id ancestry.
 
527
            graph = self.repository.get_graph()
 
528
            ancestors = graph.find_unique_ancestors(start_revision_id,
 
529
                                                    [stop_revision_id])
 
530
            for node in rev_iter:
 
531
                rev_id = node.key[-1]
 
532
                if rev_id not in ancestors:
 
533
                    continue
 
534
                yield (rev_id, node.merge_depth, node.revno,
 
535
                       node.end_of_merge)
502
536
        elif stop_rule == 'with-merges':
503
537
            stop_rev = self.repository.get_revision(stop_revision_id)
504
538
            if stop_rev.parent_ids:
527
561
        else:
528
562
            raise ValueError('invalid stop_rule %r' % stop_rule)
529
563
 
530
 
    def _filter_non_ancestors(self, rev_iter):
 
564
    def _filter_start_non_ancestors(self, rev_iter):
531
565
        # If we started from a dotted revno, we want to consider it as a tip
532
566
        # and don't want to yield revisions that are not part of its
533
567
        # ancestry. Given the order guaranteed by the merge sort, we will see
594
628
        :param other: The branch to bind to
595
629
        :type other: Branch
596
630
        """
597
 
        raise errors.UpgradeRequired(self.base)
 
631
        raise errors.UpgradeRequired(self.user_url)
598
632
 
599
633
    def set_append_revisions_only(self, enabled):
600
634
        if not self._format.supports_set_append_revisions_only():
601
 
            raise errors.UpgradeRequired(self.base)
 
635
            raise errors.UpgradeRequired(self.user_url)
602
636
        if enabled:
603
637
            value = 'True'
604
638
        else:
652
686
    def get_old_bound_location(self):
653
687
        """Return the URL of the branch we used to be bound to
654
688
        """
655
 
        raise errors.UpgradeRequired(self.base)
 
689
        raise errors.UpgradeRequired(self.user_url)
656
690
 
657
691
    def get_commit_builder(self, parents, config=None, timestamp=None,
658
692
                           timezone=None, committer=None, revprops=None,
736
770
            stacking.
737
771
        """
738
772
        if not self._format.supports_stacking():
739
 
            raise errors.UnstackableBranchFormat(self._format, self.base)
 
773
            raise errors.UnstackableBranchFormat(self._format, self.user_url)
740
774
        # XXX: Changing from one fallback repository to another does not check
741
775
        # that all the data you need is present in the new fallback.
742
776
        # Possibly it should.
772
806
            if len(old_repository._fallback_repositories) != 1:
773
807
                raise AssertionError("can't cope with fallback repositories "
774
808
                    "of %r" % (self.repository,))
775
 
            # unlock it, including unlocking the fallback
 
809
            # Open the new repository object.
 
810
            # Repositories don't offer an interface to remove fallback
 
811
            # repositories today; take the conceptually simpler option and just
 
812
            # reopen it.  We reopen it starting from the URL so that we
 
813
            # get a separate connection for RemoteRepositories and can
 
814
            # stream from one of them to the other.  This does mean doing
 
815
            # separate SSH connection setup, but unstacking is not a
 
816
            # common operation so it's tolerable.
 
817
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
 
818
            new_repository = new_bzrdir.find_repository()
 
819
            if new_repository._fallback_repositories:
 
820
                raise AssertionError("didn't expect %r to have "
 
821
                    "fallback_repositories"
 
822
                    % (self.repository,))
 
823
            # Replace self.repository with the new repository.
 
824
            # Do our best to transfer the lock state (i.e. lock-tokens and
 
825
            # lock count) of self.repository to the new repository.
 
826
            lock_token = old_repository.lock_write().repository_token
 
827
            self.repository = new_repository
 
828
            if isinstance(self, remote.RemoteBranch):
 
829
                # Remote branches can have a second reference to the old
 
830
                # repository that need to be replaced.
 
831
                if self._real_branch is not None:
 
832
                    self._real_branch.repository = new_repository
 
833
            self.repository.lock_write(token=lock_token)
 
834
            if lock_token is not None:
 
835
                old_repository.leave_lock_in_place()
776
836
            old_repository.unlock()
 
837
            if lock_token is not None:
 
838
                # XXX: self.repository.leave_lock_in_place() before this
 
839
                # function will not be preserved.  Fortunately that doesn't
 
840
                # affect the current default format (2a), and would be a
 
841
                # corner-case anyway.
 
842
                #  - Andrew Bennetts, 2010/06/30
 
843
                self.repository.dont_leave_lock_in_place()
 
844
            old_lock_count = 0
 
845
            while True:
 
846
                try:
 
847
                    old_repository.unlock()
 
848
                except errors.LockNotHeld:
 
849
                    break
 
850
                old_lock_count += 1
 
851
            if old_lock_count == 0:
 
852
                raise AssertionError(
 
853
                    'old_repository should have been locked at least once.')
 
854
            for i in range(old_lock_count-1):
 
855
                self.repository.lock_write()
 
856
            # Fetch from the old repository into the new.
777
857
            old_repository.lock_read()
778
858
            try:
779
 
                # Repositories don't offer an interface to remove fallback
780
 
                # repositories today; take the conceptually simpler option and just
781
 
                # reopen it.  We reopen it starting from the URL so that we
782
 
                # get a separate connection for RemoteRepositories and can
783
 
                # stream from one of them to the other.  This does mean doing
784
 
                # separate SSH connection setup, but unstacking is not a
785
 
                # common operation so it's tolerable.
786
 
                new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
787
 
                new_repository = new_bzrdir.find_repository()
788
 
                self.repository = new_repository
789
 
                if self.repository._fallback_repositories:
790
 
                    raise AssertionError("didn't expect %r to have "
791
 
                        "fallback_repositories"
792
 
                        % (self.repository,))
793
 
                # this is not paired with an unlock because it's just restoring
794
 
                # the previous state; the lock's released when set_stacked_on_url
795
 
                # returns
796
 
                self.repository.lock_write()
797
859
                # XXX: If you unstack a branch while it has a working tree
798
860
                # with a pending merge, the pending-merged revisions will no
799
861
                # longer be present.  You can (probably) revert and remerge.
893
955
 
894
956
    def unbind(self):
895
957
        """Older format branches cannot bind or unbind."""
896
 
        raise errors.UpgradeRequired(self.base)
 
958
        raise errors.UpgradeRequired(self.user_url)
897
959
 
898
960
    def last_revision(self):
899
961
        """Return last revision id, or NULL_REVISION."""
940
1002
                raise errors.NoSuchRevision(self, stop_revision)
941
1003
        return other_history[self_len:stop_revision]
942
1004
 
943
 
    @needs_write_lock
944
1005
    def update_revisions(self, other, stop_revision=None, overwrite=False,
945
1006
                         graph=None):
946
1007
        """Pull in new perfect-fit revisions.
995
1056
            self._extend_partial_history(distance_from_last)
996
1057
        return self._partial_revision_history_cache[distance_from_last]
997
1058
 
998
 
    @needs_write_lock
999
1059
    def pull(self, source, overwrite=False, stop_revision=None,
1000
1060
             possible_transports=None, *args, **kwargs):
1001
1061
        """Mirror source into this branch.
1059
1119
        try:
1060
1120
            return urlutils.join(self.base[:-1], parent)
1061
1121
        except errors.InvalidURLJoin, e:
1062
 
            raise errors.InaccessibleParent(parent, self.base)
 
1122
            raise errors.InaccessibleParent(parent, self.user_url)
1063
1123
 
1064
1124
    def _get_parent_location(self):
1065
1125
        raise NotImplementedError(self._get_parent_location)
1244
1304
                revno = 1
1245
1305
        destination.set_last_revision_info(revno, revision_id)
1246
1306
 
1247
 
    @needs_read_lock
1248
1307
    def copy_content_into(self, destination, revision_id=None):
1249
1308
        """Copy the content of self into destination.
1250
1309
 
1251
1310
        revision_id: if not None, the revision history in the new branch will
1252
1311
                     be truncated to end with revision_id.
1253
1312
        """
1254
 
        self.update_references(destination)
1255
 
        self._synchronize_history(destination, revision_id)
1256
 
        try:
1257
 
            parent = self.get_parent()
1258
 
        except errors.InaccessibleParent, e:
1259
 
            mutter('parent was not accessible to copy: %s', e)
1260
 
        else:
1261
 
            if parent:
1262
 
                destination.set_parent(parent)
1263
 
        if self._push_should_merge_tags():
1264
 
            self.tags.merge_to(destination.tags)
 
1313
        return InterBranch.get(self, destination).copy_content_into(
 
1314
            revision_id=revision_id)
1265
1315
 
1266
1316
    def update_references(self, target):
1267
1317
        if not getattr(self._format, 'supports_reference_locations', False):
1335
1385
        """
1336
1386
        # XXX: Fix the bzrdir API to allow getting the branch back from the
1337
1387
        # clone call. Or something. 20090224 RBC/spiv.
 
1388
        # XXX: Should this perhaps clone colocated branches as well, 
 
1389
        # rather than just the default branch? 20100319 JRV
1338
1390
        if revision_id is None:
1339
1391
            revision_id = self.last_revision()
1340
1392
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1499
1551
        try:
1500
1552
            transport = a_bzrdir.get_branch_transport(None, name=name)
1501
1553
            format_string = transport.get_bytes("format")
1502
 
            return klass._formats[format_string]
 
1554
            format = klass._formats[format_string]
 
1555
            if isinstance(format, MetaDirBranchFormatFactory):
 
1556
                return format()
 
1557
            return format
1503
1558
        except errors.NoSuchFile:
1504
1559
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1505
1560
        except KeyError:
1510
1565
        """Return the current default format."""
1511
1566
        return klass._default_format
1512
1567
 
1513
 
    def get_reference(self, a_bzrdir):
 
1568
    @classmethod
 
1569
    def get_formats(klass):
 
1570
        """Get all the known formats.
 
1571
 
 
1572
        Warning: This triggers a load of all lazy registered formats: do not
 
1573
        use except when that is desireed.
 
1574
        """
 
1575
        result = []
 
1576
        for fmt in klass._formats.values():
 
1577
            if isinstance(fmt, MetaDirBranchFormatFactory):
 
1578
                fmt = fmt()
 
1579
            result.append(fmt)
 
1580
        return result
 
1581
 
 
1582
    def get_reference(self, a_bzrdir, name=None):
1514
1583
        """Get the target reference of the branch in a_bzrdir.
1515
1584
 
1516
1585
        format probing must have been completed before calling
1518
1587
        in a_bzrdir is correct.
1519
1588
 
1520
1589
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1590
        :param name: Name of the colocated branch to fetch
1521
1591
        :return: None if the branch is not a reference branch.
1522
1592
        """
1523
1593
        return None
1524
1594
 
1525
1595
    @classmethod
1526
 
    def set_reference(self, a_bzrdir, to_branch):
 
1596
    def set_reference(self, a_bzrdir, name, to_branch):
1527
1597
        """Set the target reference of the branch in a_bzrdir.
1528
1598
 
1529
1599
        format probing must have been completed before calling
1531
1601
        in a_bzrdir is correct.
1532
1602
 
1533
1603
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1604
        :param name: Name of colocated branch to set, None for default
1534
1605
        :param to_branch: branch that the checkout is to reference
1535
1606
        """
1536
1607
        raise NotImplementedError(self.set_reference)
1564
1635
            elsewhere)
1565
1636
        :return: a branch in this format
1566
1637
        """
1567
 
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
 
1638
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1568
1639
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1569
1640
        lock_map = {
1570
1641
            'metadir': ('lock', lockdir.LockDir),
1650
1721
 
1651
1722
    @classmethod
1652
1723
    def register_format(klass, format):
1653
 
        """Register a metadir format."""
 
1724
        """Register a metadir format.
 
1725
        
 
1726
        See MetaDirBranchFormatFactory for the ability to register a format
 
1727
        without loading the code the format needs until it is actually used.
 
1728
        """
1654
1729
        klass._formats[format.get_format_string()] = format
1655
1730
        # Metadir formats have a network name of their format string, and get
1656
 
        # registered as class factories.
1657
 
        network_format_registry.register(format.get_format_string(), format.__class__)
 
1731
        # registered as factories.
 
1732
        if isinstance(format, MetaDirBranchFormatFactory):
 
1733
            network_format_registry.register(format.get_format_string(), format)
 
1734
        else:
 
1735
            network_format_registry.register(format.get_format_string(),
 
1736
                format.__class__)
1658
1737
 
1659
1738
    @classmethod
1660
1739
    def set_default_format(klass, format):
1680
1759
        return False  # by default
1681
1760
 
1682
1761
 
 
1762
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
 
1763
    """A factory for a BranchFormat object, permitting simple lazy registration.
 
1764
    
 
1765
    While none of the built in BranchFormats are lazy registered yet,
 
1766
    bzrlib.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
 
1767
    use it, and the bzr-loom plugin uses it as well (see
 
1768
    bzrlib.plugins.loom.formats).
 
1769
    """
 
1770
 
 
1771
    def __init__(self, format_string, module_name, member_name):
 
1772
        """Create a MetaDirBranchFormatFactory.
 
1773
 
 
1774
        :param format_string: The format string the format has.
 
1775
        :param module_name: Module to load the format class from.
 
1776
        :param member_name: Attribute name within the module for the format class.
 
1777
        """
 
1778
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
 
1779
        self._format_string = format_string
 
1780
        
 
1781
    def get_format_string(self):
 
1782
        """See BranchFormat.get_format_string."""
 
1783
        return self._format_string
 
1784
 
 
1785
    def __call__(self):
 
1786
        """Used for network_format_registry support."""
 
1787
        return self.get_obj()()
 
1788
 
 
1789
 
1683
1790
class BranchHooks(Hooks):
1684
1791
    """A dictionary mapping hook name to a list of callables for branch hooks.
1685
1792
 
1755
1862
            "all are called with the url returned from the previous hook."
1756
1863
            "The order is however undefined.", (1, 9), None))
1757
1864
        self.create_hook(HookPoint('automatic_tag_name',
1758
 
            "Called to determine an automatic tag name for a revision."
 
1865
            "Called to determine an automatic tag name for a revision. "
1759
1866
            "automatic_tag_name is called with (branch, revision_id) and "
1760
1867
            "should return a tag name or None if no tag name could be "
1761
1868
            "determined. The first non-None tag name returned will be used.",
1852
1959
        return self.__dict__ == other.__dict__
1853
1960
 
1854
1961
    def __repr__(self):
1855
 
        if self.branch:
1856
 
            return "<%s of %s>" % (self.__class__.__name__, self.branch)
1857
 
        else:
1858
 
            return "<%s of format:%s bzrdir:%s>" % (
1859
 
                self.__class__.__name__, self.branch,
1860
 
                self.format, self.bzrdir)
 
1962
        return "<%s of %s>" % (self.__class__.__name__, self.branch)
1861
1963
 
1862
1964
 
1863
1965
class SwitchHookParams(object):
1957
2059
            if format.__class__ != self.__class__:
1958
2060
                raise AssertionError("wrong format %r found for %r" %
1959
2061
                    (format, self))
 
2062
        transport = a_bzrdir.get_branch_transport(None, name=name)
1960
2063
        try:
1961
 
            transport = a_bzrdir.get_branch_transport(None, name=name)
1962
2064
            control_files = lockable_files.LockableFiles(transport, 'lock',
1963
2065
                                                         lockdir.LockDir)
1964
2066
            return self._branch_class()(_format=self,
2146
2248
        """See BranchFormat.get_format_description()."""
2147
2249
        return "Checkout reference format 1"
2148
2250
 
2149
 
    def get_reference(self, a_bzrdir):
 
2251
    def get_reference(self, a_bzrdir, name=None):
2150
2252
        """See BranchFormat.get_reference()."""
2151
 
        transport = a_bzrdir.get_branch_transport(None)
 
2253
        transport = a_bzrdir.get_branch_transport(None, name=name)
2152
2254
        return transport.get_bytes('location')
2153
2255
 
2154
 
    def set_reference(self, a_bzrdir, to_branch):
 
2256
    def set_reference(self, a_bzrdir, name, to_branch):
2155
2257
        """See BranchFormat.set_reference()."""
2156
 
        transport = a_bzrdir.get_branch_transport(None)
 
2258
        transport = a_bzrdir.get_branch_transport(None, name=name)
2157
2259
        location = transport.put_bytes('location', to_branch.base)
2158
2260
 
2159
2261
    def initialize(self, a_bzrdir, name=None, target_branch=None):
2162
2264
            # this format does not implement branch itself, thus the implicit
2163
2265
            # creation contract must see it as uninitializable
2164
2266
            raise errors.UninitializableFormat(self)
2165
 
        mutter('creating branch reference in %s', a_bzrdir.transport.base)
 
2267
        mutter('creating branch reference in %s', a_bzrdir.user_url)
2166
2268
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2167
2269
        branch_transport.put_bytes('location',
2168
 
            target_branch.bzrdir.root_transport.base)
 
2270
            target_branch.bzrdir.user_url)
2169
2271
        branch_transport.put_bytes('format', self.get_format_string())
2170
2272
        branch = self.open(
2171
2273
            a_bzrdir, name, _found=True,
2210
2312
                raise AssertionError("wrong format %r found for %r" %
2211
2313
                    (format, self))
2212
2314
        if location is None:
2213
 
            location = self.get_reference(a_bzrdir)
 
2315
            location = self.get_reference(a_bzrdir, name)
2214
2316
        real_bzrdir = bzrdir.BzrDir.open(
2215
2317
            location, possible_transports=possible_transports)
2216
2318
        result = real_bzrdir.open_branch(name=name, 
2254
2356
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2255
2357
 
2256
2358
 
 
2359
class BranchWriteLockResult(LogicalLockResult):
 
2360
    """The result of write locking a branch.
 
2361
 
 
2362
    :ivar branch_token: The token obtained from the underlying branch lock, or
 
2363
        None.
 
2364
    :ivar unlock: A callable which will unlock the lock.
 
2365
    """
 
2366
 
 
2367
    def __init__(self, unlock, branch_token):
 
2368
        LogicalLockResult.__init__(self, unlock)
 
2369
        self.branch_token = branch_token
 
2370
 
 
2371
    def __repr__(self):
 
2372
        return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
 
2373
            self.unlock)
 
2374
 
 
2375
 
2257
2376
class BzrBranch(Branch, _RelockDebugMixin):
2258
2377
    """A branch stored in the actual filesystem.
2259
2378
 
2293
2412
 
2294
2413
    def __str__(self):
2295
2414
        if self.name is None:
2296
 
            return '%s(%r)' % (self.__class__.__name__, self.base)
 
2415
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
2297
2416
        else:
2298
 
            return '%s(%r,%r)' % (self.__class__.__name__, self.base, self.name)
 
2417
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
 
2418
                self.name)
2299
2419
 
2300
2420
    __repr__ = __str__
2301
2421
 
2312
2432
        return self.control_files.is_locked()
2313
2433
 
2314
2434
    def lock_write(self, token=None):
 
2435
        """Lock the branch for write operations.
 
2436
 
 
2437
        :param token: A token to permit reacquiring a previously held and
 
2438
            preserved lock.
 
2439
        :return: A BranchWriteLockResult.
 
2440
        """
2315
2441
        if not self.is_locked():
2316
2442
            self._note_lock('w')
2317
2443
        # All-in-one needs to always unlock/lock.
2323
2449
        else:
2324
2450
            took_lock = False
2325
2451
        try:
2326
 
            return self.control_files.lock_write(token=token)
 
2452
            return BranchWriteLockResult(self.unlock,
 
2453
                self.control_files.lock_write(token=token))
2327
2454
        except:
2328
2455
            if took_lock:
2329
2456
                self.repository.unlock()
2330
2457
            raise
2331
2458
 
2332
2459
    def lock_read(self):
 
2460
        """Lock the branch for read operations.
 
2461
 
 
2462
        :return: A bzrlib.lock.LogicalLockResult.
 
2463
        """
2333
2464
        if not self.is_locked():
2334
2465
            self._note_lock('r')
2335
2466
        # All-in-one needs to always unlock/lock.
2342
2473
            took_lock = False
2343
2474
        try:
2344
2475
            self.control_files.lock_read()
 
2476
            return LogicalLockResult(self.unlock)
2345
2477
        except:
2346
2478
            if took_lock:
2347
2479
                self.repository.unlock()
2516
2648
        return result
2517
2649
 
2518
2650
    def get_stacked_on_url(self):
2519
 
        raise errors.UnstackableBranchFormat(self._format, self.base)
 
2651
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2520
2652
 
2521
2653
    def set_push_location(self, location):
2522
2654
        """See Branch.set_push_location."""
2712
2844
        if _mod_revision.is_null(last_revision):
2713
2845
            return
2714
2846
        if last_revision not in self._lefthand_history(revision_id):
2715
 
            raise errors.AppendRevisionsOnlyViolation(self.base)
 
2847
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
2716
2848
 
2717
2849
    def _gen_revision_history(self):
2718
2850
        """Generate the revision history from last revision
2818
2950
        if branch_location is None:
2819
2951
            return Branch.reference_parent(self, file_id, path,
2820
2952
                                           possible_transports)
2821
 
        branch_location = urlutils.join(self.base, branch_location)
 
2953
        branch_location = urlutils.join(self.user_url, branch_location)
2822
2954
        return Branch.open(branch_location,
2823
2955
                           possible_transports=possible_transports)
2824
2956
 
2939
3071
    """
2940
3072
 
2941
3073
    def get_stacked_on_url(self):
2942
 
        raise errors.UnstackableBranchFormat(self._format, self.base)
 
3074
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2943
3075
 
2944
3076
 
2945
3077
######################################################################
3032
3164
        :param verbose: Requests more detailed display of what was checked,
3033
3165
            if any.
3034
3166
        """
3035
 
        note('checked branch %s format %s', self.branch.base,
 
3167
        note('checked branch %s format %s', self.branch.user_url,
3036
3168
            self.branch._format)
3037
3169
        for error in self.errors:
3038
3170
            note('found error:%s', error)
3133
3265
    _optimisers = []
3134
3266
    """The available optimised InterBranch types."""
3135
3267
 
3136
 
    @staticmethod
3137
 
    def _get_branch_formats_to_test():
3138
 
        """Return a tuple with the Branch formats to use when testing."""
3139
 
        raise NotImplementedError(InterBranch._get_branch_formats_to_test)
 
3268
    @classmethod
 
3269
    def _get_branch_formats_to_test(klass):
 
3270
        """Return an iterable of format tuples for testing.
 
3271
        
 
3272
        :return: An iterable of (from_format, to_format) to use when testing
 
3273
            this InterBranch class. Each InterBranch class should define this
 
3274
            method itself.
 
3275
        """
 
3276
        raise NotImplementedError(klass._get_branch_formats_to_test)
3140
3277
 
 
3278
    @needs_write_lock
3141
3279
    def pull(self, overwrite=False, stop_revision=None,
3142
3280
             possible_transports=None, local=False):
3143
3281
        """Mirror source into target branch.
3148
3286
        """
3149
3287
        raise NotImplementedError(self.pull)
3150
3288
 
 
3289
    @needs_write_lock
3151
3290
    def update_revisions(self, stop_revision=None, overwrite=False,
3152
3291
                         graph=None):
3153
3292
        """Pull in new perfect-fit revisions.
3161
3300
        """
3162
3301
        raise NotImplementedError(self.update_revisions)
3163
3302
 
 
3303
    @needs_write_lock
3164
3304
    def push(self, overwrite=False, stop_revision=None,
3165
3305
             _override_hook_source_branch=None):
3166
3306
        """Mirror the source branch into the target branch.
3169
3309
        """
3170
3310
        raise NotImplementedError(self.push)
3171
3311
 
 
3312
    @needs_write_lock
 
3313
    def copy_content_into(self, revision_id=None):
 
3314
        """Copy the content of source into target
 
3315
 
 
3316
        revision_id: if not None, the revision history in the new branch will
 
3317
                     be truncated to end with revision_id.
 
3318
        """
 
3319
        raise NotImplementedError(self.copy_content_into)
 
3320
 
3172
3321
 
3173
3322
class GenericInterBranch(InterBranch):
3174
 
    """InterBranch implementation that uses public Branch functions.
3175
 
    """
3176
 
 
3177
 
    @staticmethod
3178
 
    def _get_branch_formats_to_test():
3179
 
        return BranchFormat._default_format, BranchFormat._default_format
3180
 
 
 
3323
    """InterBranch implementation that uses public Branch functions."""
 
3324
 
 
3325
    @classmethod
 
3326
    def is_compatible(klass, source, target):
 
3327
        # GenericBranch uses the public API, so always compatible
 
3328
        return True
 
3329
 
 
3330
    @classmethod
 
3331
    def _get_branch_formats_to_test(klass):
 
3332
        return [(BranchFormat._default_format, BranchFormat._default_format)]
 
3333
 
 
3334
    @classmethod
 
3335
    def unwrap_format(klass, format):
 
3336
        if isinstance(format, remote.RemoteBranchFormat):
 
3337
            format._ensure_real()
 
3338
            return format._custom_format
 
3339
        return format                                                                                                  
 
3340
 
 
3341
    @needs_write_lock
 
3342
    def copy_content_into(self, revision_id=None):
 
3343
        """Copy the content of source into target
 
3344
 
 
3345
        revision_id: if not None, the revision history in the new branch will
 
3346
                     be truncated to end with revision_id.
 
3347
        """
 
3348
        self.source.update_references(self.target)
 
3349
        self.source._synchronize_history(self.target, revision_id)
 
3350
        try:
 
3351
            parent = self.source.get_parent()
 
3352
        except errors.InaccessibleParent, e:
 
3353
            mutter('parent was not accessible to copy: %s', e)
 
3354
        else:
 
3355
            if parent:
 
3356
                self.target.set_parent(parent)
 
3357
        if self.source._push_should_merge_tags():
 
3358
            self.source.tags.merge_to(self.target.tags)
 
3359
 
 
3360
    @needs_write_lock
3181
3361
    def update_revisions(self, stop_revision=None, overwrite=False,
3182
3362
        graph=None):
3183
3363
        """See InterBranch.update_revisions()."""
3184
 
        self.source.lock_read()
3185
 
        try:
3186
 
            other_revno, other_last_revision = self.source.last_revision_info()
3187
 
            stop_revno = None # unknown
3188
 
            if stop_revision is None:
3189
 
                stop_revision = other_last_revision
3190
 
                if _mod_revision.is_null(stop_revision):
3191
 
                    # if there are no commits, we're done.
3192
 
                    return
3193
 
                stop_revno = other_revno
3194
 
 
3195
 
            # what's the current last revision, before we fetch [and change it
3196
 
            # possibly]
3197
 
            last_rev = _mod_revision.ensure_null(self.target.last_revision())
3198
 
            # we fetch here so that we don't process data twice in the common
3199
 
            # case of having something to pull, and so that the check for
3200
 
            # already merged can operate on the just fetched graph, which will
3201
 
            # be cached in memory.
3202
 
            self.target.fetch(self.source, stop_revision)
3203
 
            # Check to see if one is an ancestor of the other
3204
 
            if not overwrite:
3205
 
                if graph is None:
3206
 
                    graph = self.target.repository.get_graph()
3207
 
                if self.target._check_if_descendant_or_diverged(
3208
 
                        stop_revision, last_rev, graph, self.source):
3209
 
                    # stop_revision is a descendant of last_rev, but we aren't
3210
 
                    # overwriting, so we're done.
3211
 
                    return
3212
 
            if stop_revno is None:
3213
 
                if graph is None:
3214
 
                    graph = self.target.repository.get_graph()
3215
 
                this_revno, this_last_revision = \
3216
 
                        self.target.last_revision_info()
3217
 
                stop_revno = graph.find_distance_to_null(stop_revision,
3218
 
                                [(other_last_revision, other_revno),
3219
 
                                 (this_last_revision, this_revno)])
3220
 
            self.target.set_last_revision_info(stop_revno, stop_revision)
3221
 
        finally:
3222
 
            self.source.unlock()
3223
 
 
 
3364
        other_revno, other_last_revision = self.source.last_revision_info()
 
3365
        stop_revno = None # unknown
 
3366
        if stop_revision is None:
 
3367
            stop_revision = other_last_revision
 
3368
            if _mod_revision.is_null(stop_revision):
 
3369
                # if there are no commits, we're done.
 
3370
                return
 
3371
            stop_revno = other_revno
 
3372
 
 
3373
        # what's the current last revision, before we fetch [and change it
 
3374
        # possibly]
 
3375
        last_rev = _mod_revision.ensure_null(self.target.last_revision())
 
3376
        # we fetch here so that we don't process data twice in the common
 
3377
        # case of having something to pull, and so that the check for
 
3378
        # already merged can operate on the just fetched graph, which will
 
3379
        # be cached in memory.
 
3380
        self.target.fetch(self.source, stop_revision)
 
3381
        # Check to see if one is an ancestor of the other
 
3382
        if not overwrite:
 
3383
            if graph is None:
 
3384
                graph = self.target.repository.get_graph()
 
3385
            if self.target._check_if_descendant_or_diverged(
 
3386
                    stop_revision, last_rev, graph, self.source):
 
3387
                # stop_revision is a descendant of last_rev, but we aren't
 
3388
                # overwriting, so we're done.
 
3389
                return
 
3390
        if stop_revno is None:
 
3391
            if graph is None:
 
3392
                graph = self.target.repository.get_graph()
 
3393
            this_revno, this_last_revision = \
 
3394
                    self.target.last_revision_info()
 
3395
            stop_revno = graph.find_distance_to_null(stop_revision,
 
3396
                            [(other_last_revision, other_revno),
 
3397
                             (this_last_revision, this_revno)])
 
3398
        self.target.set_last_revision_info(stop_revno, stop_revision)
 
3399
 
 
3400
    @needs_write_lock
3224
3401
    def pull(self, overwrite=False, stop_revision=None,
3225
 
             possible_transports=None, _hook_master=None, run_hooks=True,
 
3402
             possible_transports=None, run_hooks=True,
3226
3403
             _override_hook_target=None, local=False):
3227
 
        """See Branch.pull.
 
3404
        """Pull from source into self, updating my master if any.
3228
3405
 
3229
 
        :param _hook_master: Private parameter - set the branch to
3230
 
            be supplied as the master to pull hooks.
3231
3406
        :param run_hooks: Private parameter - if false, this branch
3232
3407
            is being called because it's the master of the primary branch,
3233
3408
            so it should not run its hooks.
3234
 
        :param _override_hook_target: Private parameter - set the branch to be
3235
 
            supplied as the target_branch to pull hooks.
3236
 
        :param local: Only update the local branch, and not the bound branch.
3237
3409
        """
3238
 
        # This type of branch can't be bound.
3239
 
        if local:
 
3410
        bound_location = self.target.get_bound_location()
 
3411
        if local and not bound_location:
3240
3412
            raise errors.LocalRequiresBoundBranch()
3241
 
        result = PullResult()
3242
 
        result.source_branch = self.source
3243
 
        if _override_hook_target is None:
3244
 
            result.target_branch = self.target
3245
 
        else:
3246
 
            result.target_branch = _override_hook_target
3247
 
        self.source.lock_read()
 
3413
        master_branch = None
 
3414
        if not local and bound_location and self.source.user_url != bound_location:
 
3415
            # not pulling from master, so we need to update master.
 
3416
            master_branch = self.target.get_master_branch(possible_transports)
 
3417
            master_branch.lock_write()
3248
3418
        try:
3249
 
            # We assume that during 'pull' the target repository is closer than
3250
 
            # the source one.
3251
 
            self.source.update_references(self.target)
3252
 
            graph = self.target.repository.get_graph(self.source.repository)
3253
 
            # TODO: Branch formats should have a flag that indicates 
3254
 
            # that revno's are expensive, and pull() should honor that flag.
3255
 
            # -- JRV20090506
3256
 
            result.old_revno, result.old_revid = \
3257
 
                self.target.last_revision_info()
3258
 
            self.target.update_revisions(self.source, stop_revision,
3259
 
                overwrite=overwrite, graph=graph)
3260
 
            # TODO: The old revid should be specified when merging tags, 
3261
 
            # so a tags implementation that versions tags can only 
3262
 
            # pull in the most recent changes. -- JRV20090506
3263
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3264
 
                overwrite)
3265
 
            result.new_revno, result.new_revid = self.target.last_revision_info()
3266
 
            if _hook_master:
3267
 
                result.master_branch = _hook_master
3268
 
                result.local_branch = result.target_branch
3269
 
            else:
3270
 
                result.master_branch = result.target_branch
3271
 
                result.local_branch = None
3272
 
            if run_hooks:
3273
 
                for hook in Branch.hooks['post_pull']:
3274
 
                    hook(result)
 
3419
            if master_branch:
 
3420
                # pull from source into master.
 
3421
                master_branch.pull(self.source, overwrite, stop_revision,
 
3422
                    run_hooks=False)
 
3423
            return self._pull(overwrite,
 
3424
                stop_revision, _hook_master=master_branch,
 
3425
                run_hooks=run_hooks,
 
3426
                _override_hook_target=_override_hook_target)
3275
3427
        finally:
3276
 
            self.source.unlock()
3277
 
        return result
 
3428
            if master_branch:
 
3429
                master_branch.unlock()
3278
3430
 
3279
3431
    def push(self, overwrite=False, stop_revision=None,
3280
3432
             _override_hook_source_branch=None):
3342
3494
            _run_hooks()
3343
3495
            return result
3344
3496
 
3345
 
    @classmethod
3346
 
    def is_compatible(self, source, target):
3347
 
        # GenericBranch uses the public API, so always compatible
3348
 
        return True
3349
 
 
3350
 
 
3351
 
class InterToBranch5(GenericInterBranch):
3352
 
 
3353
 
    @staticmethod
3354
 
    def _get_branch_formats_to_test():
3355
 
        return BranchFormat._default_format, BzrBranchFormat5()
3356
 
 
3357
 
    def pull(self, overwrite=False, stop_revision=None,
3358
 
             possible_transports=None, run_hooks=True,
 
3497
    def _pull(self, overwrite=False, stop_revision=None,
 
3498
             possible_transports=None, _hook_master=None, run_hooks=True,
3359
3499
             _override_hook_target=None, local=False):
3360
 
        """Pull from source into self, updating my master if any.
3361
 
 
 
3500
        """See Branch.pull.
 
3501
 
 
3502
        This function is the core worker, used by GenericInterBranch.pull to
 
3503
        avoid duplication when pulling source->master and source->local.
 
3504
 
 
3505
        :param _hook_master: Private parameter - set the branch to
 
3506
            be supplied as the master to pull hooks.
3362
3507
        :param run_hooks: Private parameter - if false, this branch
3363
3508
            is being called because it's the master of the primary branch,
3364
3509
            so it should not run its hooks.
 
3510
        :param _override_hook_target: Private parameter - set the branch to be
 
3511
            supplied as the target_branch to pull hooks.
 
3512
        :param local: Only update the local branch, and not the bound branch.
3365
3513
        """
3366
 
        bound_location = self.target.get_bound_location()
3367
 
        if local and not bound_location:
 
3514
        # This type of branch can't be bound.
 
3515
        if local:
3368
3516
            raise errors.LocalRequiresBoundBranch()
3369
 
        master_branch = None
3370
 
        if not local and bound_location and self.source.base != bound_location:
3371
 
            # not pulling from master, so we need to update master.
3372
 
            master_branch = self.target.get_master_branch(possible_transports)
3373
 
            master_branch.lock_write()
 
3517
        result = PullResult()
 
3518
        result.source_branch = self.source
 
3519
        if _override_hook_target is None:
 
3520
            result.target_branch = self.target
 
3521
        else:
 
3522
            result.target_branch = _override_hook_target
 
3523
        self.source.lock_read()
3374
3524
        try:
3375
 
            if master_branch:
3376
 
                # pull from source into master.
3377
 
                master_branch.pull(self.source, overwrite, stop_revision,
3378
 
                    run_hooks=False)
3379
 
            return super(InterToBranch5, self).pull(overwrite,
3380
 
                stop_revision, _hook_master=master_branch,
3381
 
                run_hooks=run_hooks,
3382
 
                _override_hook_target=_override_hook_target)
 
3525
            # We assume that during 'pull' the target repository is closer than
 
3526
            # the source one.
 
3527
            self.source.update_references(self.target)
 
3528
            graph = self.target.repository.get_graph(self.source.repository)
 
3529
            # TODO: Branch formats should have a flag that indicates 
 
3530
            # that revno's are expensive, and pull() should honor that flag.
 
3531
            # -- JRV20090506
 
3532
            result.old_revno, result.old_revid = \
 
3533
                self.target.last_revision_info()
 
3534
            self.target.update_revisions(self.source, stop_revision,
 
3535
                overwrite=overwrite, graph=graph)
 
3536
            # TODO: The old revid should be specified when merging tags, 
 
3537
            # so a tags implementation that versions tags can only 
 
3538
            # pull in the most recent changes. -- JRV20090506
 
3539
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3540
                overwrite)
 
3541
            result.new_revno, result.new_revid = self.target.last_revision_info()
 
3542
            if _hook_master:
 
3543
                result.master_branch = _hook_master
 
3544
                result.local_branch = result.target_branch
 
3545
            else:
 
3546
                result.master_branch = result.target_branch
 
3547
                result.local_branch = None
 
3548
            if run_hooks:
 
3549
                for hook in Branch.hooks['post_pull']:
 
3550
                    hook(result)
3383
3551
        finally:
3384
 
            if master_branch:
3385
 
                master_branch.unlock()
 
3552
            self.source.unlock()
 
3553
        return result
3386
3554
 
3387
3555
 
3388
3556
InterBranch.register_optimiser(GenericInterBranch)
3389
 
InterBranch.register_optimiser(InterToBranch5)