/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
        errors,
30
30
        lockdir,
31
31
        lockable_files,
 
32
        remote,
32
33
        repository,
33
34
        revision as _mod_revision,
34
35
        rio,
49
50
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
50
51
from bzrlib.hooks import HookPoint, Hooks
51
52
from bzrlib.inter import InterObject
52
 
from bzrlib.lock import _RelockDebugMixin
 
53
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
53
54
from bzrlib import registry
54
55
from bzrlib.symbol_versioning import (
55
56
    deprecated_in,
63
64
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
64
65
 
65
66
 
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):
 
67
class Branch(bzrdir.ControlComponent):
79
68
    """Branch holding a history of revisions.
80
69
 
81
 
    base
82
 
        Base directory/url of the branch.
 
70
    :ivar base:
 
71
        Base directory/url of the branch; using control_url and
 
72
        control_transport is more standardized.
83
73
 
84
74
    hooks: An instance of BranchHooks.
85
75
    """
87
77
    # - RBC 20060112
88
78
    base = None
89
79
 
 
80
    @property
 
81
    def control_transport(self):
 
82
        return self._transport
 
83
 
 
84
    @property
 
85
    def user_transport(self):
 
86
        return self.bzrdir.user_transport
 
87
 
90
88
    def __init__(self, *ignored, **ignored_too):
91
89
        self.tags = self._format.make_tags(self)
92
90
        self._revision_history_cache = None
107
105
        """Activate the branch/repository from url as a fallback repository."""
108
106
        repo = self._get_fallback_repository(url)
109
107
        if repo.has_same_location(self.repository):
110
 
            raise errors.UnstackableLocationError(self.base, url)
 
108
            raise errors.UnstackableLocationError(self.user_url, url)
111
109
        self.repository.add_fallback_repository(repo)
112
110
 
113
111
    def break_lock(self):
200
198
        return self.supports_tags() and self.tags.get_tag_dict()
201
199
 
202
200
    def get_config(self):
 
201
        """Get a bzrlib.config.BranchConfig for this Branch.
 
202
 
 
203
        This can then be used to get and set configuration options for the
 
204
        branch.
 
205
 
 
206
        :return: A bzrlib.config.BranchConfig.
 
207
        """
203
208
        return BranchConfig(self)
204
209
 
205
210
    def _get_config(self):
286
291
        new_history.reverse()
287
292
        return new_history
288
293
 
289
 
    def lock_write(self):
 
294
    def lock_write(self, token=None):
 
295
        """Lock the branch for write operations.
 
296
 
 
297
        :param token: A token to permit reacquiring a previously held and
 
298
            preserved lock.
 
299
        :return: A BranchWriteLockResult.
 
300
        """
290
301
        raise NotImplementedError(self.lock_write)
291
302
 
292
303
    def lock_read(self):
 
304
        """Lock the branch for read operations.
 
305
 
 
306
        :return: A bzrlib.lock.LogicalLockResult.
 
307
        """
293
308
        raise NotImplementedError(self.lock_read)
294
309
 
295
310
    def unlock(self):
420
435
            * 'include' - the stop revision is the last item in the result
421
436
            * 'with-merges' - include the stop revision and all of its
422
437
              merged revisions in the result
 
438
            * 'with-merges-without-common-ancestry' - filter out revisions 
 
439
              that are in both ancestries
423
440
        :param direction: either 'reverse' or 'forward':
424
441
            * reverse means return the start_revision_id first, i.e.
425
442
              start at the most recent revision and go backwards in history
456
473
            stop_revision_id, stop_rule)
457
474
        # Make sure we don't return revisions that are not part of the
458
475
        # start_revision_id ancestry.
459
 
        filtered = self._filter_non_ancestors(filtered)
 
476
        filtered = self._filter_start_non_ancestors(filtered)
460
477
        if direction == 'reverse':
461
478
            return filtered
462
479
        if direction == 'forward':
499
516
                       node.end_of_merge)
500
517
                if rev_id == stop_revision_id:
501
518
                    return
 
519
        elif stop_rule == 'with-merges-without-common-ancestry':
 
520
            # We want to exclude all revisions that are already part of the
 
521
            # stop_revision_id ancestry.
 
522
            graph = self.repository.get_graph()
 
523
            ancestors = graph.find_unique_ancestors(start_revision_id,
 
524
                                                    [stop_revision_id])
 
525
            for node in rev_iter:
 
526
                rev_id = node.key[-1]
 
527
                if rev_id not in ancestors:
 
528
                    continue
 
529
                yield (rev_id, node.merge_depth, node.revno,
 
530
                       node.end_of_merge)
502
531
        elif stop_rule == 'with-merges':
503
532
            stop_rev = self.repository.get_revision(stop_revision_id)
504
533
            if stop_rev.parent_ids:
527
556
        else:
528
557
            raise ValueError('invalid stop_rule %r' % stop_rule)
529
558
 
530
 
    def _filter_non_ancestors(self, rev_iter):
 
559
    def _filter_start_non_ancestors(self, rev_iter):
531
560
        # If we started from a dotted revno, we want to consider it as a tip
532
561
        # and don't want to yield revisions that are not part of its
533
562
        # ancestry. Given the order guaranteed by the merge sort, we will see
594
623
        :param other: The branch to bind to
595
624
        :type other: Branch
596
625
        """
597
 
        raise errors.UpgradeRequired(self.base)
 
626
        raise errors.UpgradeRequired(self.user_url)
598
627
 
599
628
    def set_append_revisions_only(self, enabled):
600
629
        if not self._format.supports_set_append_revisions_only():
601
 
            raise errors.UpgradeRequired(self.base)
 
630
            raise errors.UpgradeRequired(self.user_url)
602
631
        if enabled:
603
632
            value = 'True'
604
633
        else:
652
681
    def get_old_bound_location(self):
653
682
        """Return the URL of the branch we used to be bound to
654
683
        """
655
 
        raise errors.UpgradeRequired(self.base)
 
684
        raise errors.UpgradeRequired(self.user_url)
656
685
 
657
686
    def get_commit_builder(self, parents, config=None, timestamp=None,
658
687
                           timezone=None, committer=None, revprops=None,
736
765
            stacking.
737
766
        """
738
767
        if not self._format.supports_stacking():
739
 
            raise errors.UnstackableBranchFormat(self._format, self.base)
 
768
            raise errors.UnstackableBranchFormat(self._format, self.user_url)
740
769
        # XXX: Changing from one fallback repository to another does not check
741
770
        # that all the data you need is present in the new fallback.
742
771
        # Possibly it should.
772
801
            if len(old_repository._fallback_repositories) != 1:
773
802
                raise AssertionError("can't cope with fallback repositories "
774
803
                    "of %r" % (self.repository,))
775
 
            # unlock it, including unlocking the fallback
 
804
            # Open the new repository object.
 
805
            # Repositories don't offer an interface to remove fallback
 
806
            # repositories today; take the conceptually simpler option and just
 
807
            # reopen it.  We reopen it starting from the URL so that we
 
808
            # get a separate connection for RemoteRepositories and can
 
809
            # stream from one of them to the other.  This does mean doing
 
810
            # separate SSH connection setup, but unstacking is not a
 
811
            # common operation so it's tolerable.
 
812
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
 
813
            new_repository = new_bzrdir.find_repository()
 
814
            if new_repository._fallback_repositories:
 
815
                raise AssertionError("didn't expect %r to have "
 
816
                    "fallback_repositories"
 
817
                    % (self.repository,))
 
818
            # Replace self.repository with the new repository.
 
819
            # Do our best to transfer the lock state (i.e. lock-tokens and
 
820
            # lock count) of self.repository to the new repository.
 
821
            lock_token = old_repository.lock_write().repository_token
 
822
            self.repository = new_repository
 
823
            if isinstance(self, remote.RemoteBranch):
 
824
                # Remote branches can have a second reference to the old
 
825
                # repository that need to be replaced.
 
826
                if self._real_branch is not None:
 
827
                    self._real_branch.repository = new_repository
 
828
            self.repository.lock_write(token=lock_token)
 
829
            if lock_token is not None:
 
830
                old_repository.leave_lock_in_place()
776
831
            old_repository.unlock()
 
832
            if lock_token is not None:
 
833
                # XXX: self.repository.leave_lock_in_place() before this
 
834
                # function will not be preserved.  Fortunately that doesn't
 
835
                # affect the current default format (2a), and would be a
 
836
                # corner-case anyway.
 
837
                #  - Andrew Bennetts, 2010/06/30
 
838
                self.repository.dont_leave_lock_in_place()
 
839
            old_lock_count = 0
 
840
            while True:
 
841
                try:
 
842
                    old_repository.unlock()
 
843
                except errors.LockNotHeld:
 
844
                    break
 
845
                old_lock_count += 1
 
846
            if old_lock_count == 0:
 
847
                raise AssertionError(
 
848
                    'old_repository should have been locked at least once.')
 
849
            for i in range(old_lock_count-1):
 
850
                self.repository.lock_write()
 
851
            # Fetch from the old repository into the new.
777
852
            old_repository.lock_read()
778
853
            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
854
                # XXX: If you unstack a branch while it has a working tree
798
855
                # with a pending merge, the pending-merged revisions will no
799
856
                # longer be present.  You can (probably) revert and remerge.
893
950
 
894
951
    def unbind(self):
895
952
        """Older format branches cannot bind or unbind."""
896
 
        raise errors.UpgradeRequired(self.base)
 
953
        raise errors.UpgradeRequired(self.user_url)
897
954
 
898
955
    def last_revision(self):
899
956
        """Return last revision id, or NULL_REVISION."""
940
997
                raise errors.NoSuchRevision(self, stop_revision)
941
998
        return other_history[self_len:stop_revision]
942
999
 
943
 
    @needs_write_lock
944
1000
    def update_revisions(self, other, stop_revision=None, overwrite=False,
945
1001
                         graph=None):
946
1002
        """Pull in new perfect-fit revisions.
995
1051
            self._extend_partial_history(distance_from_last)
996
1052
        return self._partial_revision_history_cache[distance_from_last]
997
1053
 
998
 
    @needs_write_lock
999
1054
    def pull(self, source, overwrite=False, stop_revision=None,
1000
1055
             possible_transports=None, *args, **kwargs):
1001
1056
        """Mirror source into this branch.
1059
1114
        try:
1060
1115
            return urlutils.join(self.base[:-1], parent)
1061
1116
        except errors.InvalidURLJoin, e:
1062
 
            raise errors.InaccessibleParent(parent, self.base)
 
1117
            raise errors.InaccessibleParent(parent, self.user_url)
1063
1118
 
1064
1119
    def _get_parent_location(self):
1065
1120
        raise NotImplementedError(self._get_parent_location)
1244
1299
                revno = 1
1245
1300
        destination.set_last_revision_info(revno, revision_id)
1246
1301
 
1247
 
    @needs_read_lock
1248
1302
    def copy_content_into(self, destination, revision_id=None):
1249
1303
        """Copy the content of self into destination.
1250
1304
 
1251
1305
        revision_id: if not None, the revision history in the new branch will
1252
1306
                     be truncated to end with revision_id.
1253
1307
        """
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)
 
1308
        return InterBranch.get(self, destination).copy_content_into(
 
1309
            revision_id=revision_id)
1265
1310
 
1266
1311
    def update_references(self, target):
1267
1312
        if not getattr(self._format, 'supports_reference_locations', False):
1335
1380
        """
1336
1381
        # XXX: Fix the bzrdir API to allow getting the branch back from the
1337
1382
        # clone call. Or something. 20090224 RBC/spiv.
 
1383
        # XXX: Should this perhaps clone colocated branches as well, 
 
1384
        # rather than just the default branch? 20100319 JRV
1338
1385
        if revision_id is None:
1339
1386
            revision_id = self.last_revision()
1340
1387
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1499
1546
        try:
1500
1547
            transport = a_bzrdir.get_branch_transport(None, name=name)
1501
1548
            format_string = transport.get_bytes("format")
1502
 
            return klass._formats[format_string]
 
1549
            format = klass._formats[format_string]
 
1550
            if isinstance(format, MetaDirBranchFormatFactory):
 
1551
                return format()
 
1552
            return format
1503
1553
        except errors.NoSuchFile:
1504
1554
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1505
1555
        except KeyError:
1510
1560
        """Return the current default format."""
1511
1561
        return klass._default_format
1512
1562
 
1513
 
    def get_reference(self, a_bzrdir):
 
1563
    @classmethod
 
1564
    def get_formats(klass):
 
1565
        """Get all the known formats.
 
1566
 
 
1567
        Warning: This triggers a load of all lazy registered formats: do not
 
1568
        use except when that is desireed.
 
1569
        """
 
1570
        result = []
 
1571
        for fmt in klass._formats.values():
 
1572
            if isinstance(fmt, MetaDirBranchFormatFactory):
 
1573
                fmt = fmt()
 
1574
            result.append(fmt)
 
1575
        return result
 
1576
 
 
1577
    def get_reference(self, a_bzrdir, name=None):
1514
1578
        """Get the target reference of the branch in a_bzrdir.
1515
1579
 
1516
1580
        format probing must have been completed before calling
1518
1582
        in a_bzrdir is correct.
1519
1583
 
1520
1584
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1585
        :param name: Name of the colocated branch to fetch
1521
1586
        :return: None if the branch is not a reference branch.
1522
1587
        """
1523
1588
        return None
1524
1589
 
1525
1590
    @classmethod
1526
 
    def set_reference(self, a_bzrdir, to_branch):
 
1591
    def set_reference(self, a_bzrdir, name, to_branch):
1527
1592
        """Set the target reference of the branch in a_bzrdir.
1528
1593
 
1529
1594
        format probing must have been completed before calling
1531
1596
        in a_bzrdir is correct.
1532
1597
 
1533
1598
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1599
        :param name: Name of colocated branch to set, None for default
1534
1600
        :param to_branch: branch that the checkout is to reference
1535
1601
        """
1536
1602
        raise NotImplementedError(self.set_reference)
1543
1609
        """Return the short format description for this format."""
1544
1610
        raise NotImplementedError(self.get_format_description)
1545
1611
 
 
1612
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
 
1613
        hooks = Branch.hooks['post_branch_init']
 
1614
        if not hooks:
 
1615
            return
 
1616
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
 
1617
        for hook in hooks:
 
1618
            hook(params)
 
1619
 
1546
1620
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1547
1621
                           lock_type='metadir', set_format=True):
1548
1622
        """Initialize a branch in a bzrdir, with specified files
1556
1630
            elsewhere)
1557
1631
        :return: a branch in this format
1558
1632
        """
1559
 
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
 
1633
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1560
1634
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1561
1635
        lock_map = {
1562
1636
            'metadir': ('lock', lockdir.LockDir),
1584
1658
        finally:
1585
1659
            if lock_taken:
1586
1660
                control_files.unlock()
1587
 
        return self.open(a_bzrdir, name, _found=True)
 
1661
        branch = self.open(a_bzrdir, name, _found=True)
 
1662
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
1663
        return branch
1588
1664
 
1589
1665
    def initialize(self, a_bzrdir, name=None):
1590
1666
        """Create a branch of this format in a_bzrdir.
1640
1716
 
1641
1717
    @classmethod
1642
1718
    def register_format(klass, format):
1643
 
        """Register a metadir format."""
 
1719
        """Register a metadir format.
 
1720
        
 
1721
        See MetaDirBranchFormatFactory for the ability to register a format
 
1722
        without loading the code the format needs until it is actually used.
 
1723
        """
1644
1724
        klass._formats[format.get_format_string()] = format
1645
1725
        # Metadir formats have a network name of their format string, and get
1646
 
        # registered as class factories.
1647
 
        network_format_registry.register(format.get_format_string(), format.__class__)
 
1726
        # registered as factories.
 
1727
        if isinstance(format, MetaDirBranchFormatFactory):
 
1728
            network_format_registry.register(format.get_format_string(), format)
 
1729
        else:
 
1730
            network_format_registry.register(format.get_format_string(),
 
1731
                format.__class__)
1648
1732
 
1649
1733
    @classmethod
1650
1734
    def set_default_format(klass, format):
1670
1754
        return False  # by default
1671
1755
 
1672
1756
 
 
1757
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
 
1758
    """A factory for a BranchFormat object, permitting simple lazy registration.
 
1759
    
 
1760
    While none of the built in BranchFormats are lazy registered yet,
 
1761
    bzrlib.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
 
1762
    use it, and the bzr-loom plugin uses it as well (see
 
1763
    bzrlib.plugins.loom.formats).
 
1764
    """
 
1765
 
 
1766
    def __init__(self, format_string, module_name, member_name):
 
1767
        """Create a MetaDirBranchFormatFactory.
 
1768
 
 
1769
        :param format_string: The format string the format has.
 
1770
        :param module_name: Module to load the format class from.
 
1771
        :param member_name: Attribute name within the module for the format class.
 
1772
        """
 
1773
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
 
1774
        self._format_string = format_string
 
1775
        
 
1776
    def get_format_string(self):
 
1777
        """See BranchFormat.get_format_string."""
 
1778
        return self._format_string
 
1779
 
 
1780
    def __call__(self):
 
1781
        """Used for network_format_registry support."""
 
1782
        return self.get_obj()()
 
1783
 
 
1784
 
1673
1785
class BranchHooks(Hooks):
1674
1786
    """A dictionary mapping hook name to a list of callables for branch hooks.
1675
1787
 
1750
1862
            "should return a tag name or None if no tag name could be "
1751
1863
            "determined. The first non-None tag name returned will be used.",
1752
1864
            (2, 2), None))
 
1865
        self.create_hook(HookPoint('post_branch_init',
 
1866
            "Called after new branch initialization completes. "
 
1867
            "post_branch_init is called with a "
 
1868
            "bzrlib.branch.BranchInitHookParams. "
 
1869
            "Note that init, branch and checkout (both heavyweight and "
 
1870
            "lightweight) will all trigger this hook.", (2, 2), None))
 
1871
        self.create_hook(HookPoint('post_switch',
 
1872
            "Called after a checkout switches branch. "
 
1873
            "post_switch is called with a "
 
1874
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
1753
1875
 
1754
1876
 
1755
1877
 
1795
1917
            self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1796
1918
 
1797
1919
 
 
1920
class BranchInitHookParams(object):
 
1921
    """Object holding parameters passed to *_branch_init hooks.
 
1922
 
 
1923
    There are 4 fields that hooks may wish to access:
 
1924
 
 
1925
    :ivar format: the branch format
 
1926
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
 
1927
    :ivar name: name of colocated branch, if any (or None)
 
1928
    :ivar branch: the branch created
 
1929
 
 
1930
    Note that for lightweight checkouts, the bzrdir and format fields refer to
 
1931
    the checkout, hence they are different from the corresponding fields in
 
1932
    branch, which refer to the original branch.
 
1933
    """
 
1934
 
 
1935
    def __init__(self, format, a_bzrdir, name, branch):
 
1936
        """Create a group of BranchInitHook parameters.
 
1937
 
 
1938
        :param format: the branch format
 
1939
        :param a_bzrdir: the BzrDir where the branch will be/has been
 
1940
            initialized
 
1941
        :param name: name of colocated branch, if any (or None)
 
1942
        :param branch: the branch created
 
1943
 
 
1944
        Note that for lightweight checkouts, the bzrdir and format fields refer
 
1945
        to the checkout, hence they are different from the corresponding fields
 
1946
        in branch, which refer to the original branch.
 
1947
        """
 
1948
        self.format = format
 
1949
        self.bzrdir = a_bzrdir
 
1950
        self.name = name
 
1951
        self.branch = branch
 
1952
 
 
1953
    def __eq__(self, other):
 
1954
        return self.__dict__ == other.__dict__
 
1955
 
 
1956
    def __repr__(self):
 
1957
        if self.branch:
 
1958
            return "<%s of %s>" % (self.__class__.__name__, self.branch)
 
1959
        else:
 
1960
            return "<%s of format:%s bzrdir:%s>" % (
 
1961
                self.__class__.__name__, self.branch,
 
1962
                self.format, self.bzrdir)
 
1963
 
 
1964
 
 
1965
class SwitchHookParams(object):
 
1966
    """Object holding parameters passed to *_switch hooks.
 
1967
 
 
1968
    There are 4 fields that hooks may wish to access:
 
1969
 
 
1970
    :ivar control_dir: BzrDir of the checkout to change
 
1971
    :ivar to_branch: branch that the checkout is to reference
 
1972
    :ivar force: skip the check for local commits in a heavy checkout
 
1973
    :ivar revision_id: revision ID to switch to (or None)
 
1974
    """
 
1975
 
 
1976
    def __init__(self, control_dir, to_branch, force, revision_id):
 
1977
        """Create a group of SwitchHook parameters.
 
1978
 
 
1979
        :param control_dir: BzrDir of the checkout to change
 
1980
        :param to_branch: branch that the checkout is to reference
 
1981
        :param force: skip the check for local commits in a heavy checkout
 
1982
        :param revision_id: revision ID to switch to (or None)
 
1983
        """
 
1984
        self.control_dir = control_dir
 
1985
        self.to_branch = to_branch
 
1986
        self.force = force
 
1987
        self.revision_id = revision_id
 
1988
 
 
1989
    def __eq__(self, other):
 
1990
        return self.__dict__ == other.__dict__
 
1991
 
 
1992
    def __repr__(self):
 
1993
        return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
 
1994
            self.control_dir, self.to_branch,
 
1995
            self.revision_id)
 
1996
 
 
1997
 
1798
1998
class BzrBranchFormat4(BranchFormat):
1799
1999
    """Bzr branch format 4.
1800
2000
 
1859
2059
            if format.__class__ != self.__class__:
1860
2060
                raise AssertionError("wrong format %r found for %r" %
1861
2061
                    (format, self))
 
2062
        transport = a_bzrdir.get_branch_transport(None, name=name)
1862
2063
        try:
1863
 
            transport = a_bzrdir.get_branch_transport(None, name=name)
1864
2064
            control_files = lockable_files.LockableFiles(transport, 'lock',
1865
2065
                                                         lockdir.LockDir)
1866
2066
            return self._branch_class()(_format=self,
2048
2248
        """See BranchFormat.get_format_description()."""
2049
2249
        return "Checkout reference format 1"
2050
2250
 
2051
 
    def get_reference(self, a_bzrdir):
 
2251
    def get_reference(self, a_bzrdir, name=None):
2052
2252
        """See BranchFormat.get_reference()."""
2053
 
        transport = a_bzrdir.get_branch_transport(None)
 
2253
        transport = a_bzrdir.get_branch_transport(None, name=name)
2054
2254
        return transport.get_bytes('location')
2055
2255
 
2056
 
    def set_reference(self, a_bzrdir, to_branch):
 
2256
    def set_reference(self, a_bzrdir, name, to_branch):
2057
2257
        """See BranchFormat.set_reference()."""
2058
 
        transport = a_bzrdir.get_branch_transport(None)
 
2258
        transport = a_bzrdir.get_branch_transport(None, name=name)
2059
2259
        location = transport.put_bytes('location', to_branch.base)
2060
2260
 
2061
2261
    def initialize(self, a_bzrdir, name=None, target_branch=None):
2064
2264
            # this format does not implement branch itself, thus the implicit
2065
2265
            # creation contract must see it as uninitializable
2066
2266
            raise errors.UninitializableFormat(self)
2067
 
        mutter('creating branch reference in %s', a_bzrdir.transport.base)
 
2267
        mutter('creating branch reference in %s', a_bzrdir.user_url)
2068
2268
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2069
2269
        branch_transport.put_bytes('location',
2070
 
            target_branch.bzrdir.root_transport.base)
 
2270
            target_branch.bzrdir.user_url)
2071
2271
        branch_transport.put_bytes('format', self.get_format_string())
2072
 
        return self.open(
 
2272
        branch = self.open(
2073
2273
            a_bzrdir, name, _found=True,
2074
2274
            possible_transports=[target_branch.bzrdir.root_transport])
 
2275
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
2276
        return branch
2075
2277
 
2076
2278
    def __init__(self):
2077
2279
        super(BranchReferenceFormat, self).__init__()
2110
2312
                raise AssertionError("wrong format %r found for %r" %
2111
2313
                    (format, self))
2112
2314
        if location is None:
2113
 
            location = self.get_reference(a_bzrdir)
 
2315
            location = self.get_reference(a_bzrdir, name)
2114
2316
        real_bzrdir = bzrdir.BzrDir.open(
2115
2317
            location, possible_transports=possible_transports)
2116
2318
        result = real_bzrdir.open_branch(name=name, 
2154
2356
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2155
2357
 
2156
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
 
2157
2376
class BzrBranch(Branch, _RelockDebugMixin):
2158
2377
    """A branch stored in the actual filesystem.
2159
2378
 
2193
2412
 
2194
2413
    def __str__(self):
2195
2414
        if self.name is None:
2196
 
            return '%s(%r)' % (self.__class__.__name__, self.base)
 
2415
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
2197
2416
        else:
2198
 
            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)
2199
2419
 
2200
2420
    __repr__ = __str__
2201
2421
 
2212
2432
        return self.control_files.is_locked()
2213
2433
 
2214
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
        """
2215
2441
        if not self.is_locked():
2216
2442
            self._note_lock('w')
2217
2443
        # All-in-one needs to always unlock/lock.
2223
2449
        else:
2224
2450
            took_lock = False
2225
2451
        try:
2226
 
            return self.control_files.lock_write(token=token)
 
2452
            return BranchWriteLockResult(self.unlock,
 
2453
                self.control_files.lock_write(token=token))
2227
2454
        except:
2228
2455
            if took_lock:
2229
2456
                self.repository.unlock()
2230
2457
            raise
2231
2458
 
2232
2459
    def lock_read(self):
 
2460
        """Lock the branch for read operations.
 
2461
 
 
2462
        :return: A bzrlib.lock.LogicalLockResult.
 
2463
        """
2233
2464
        if not self.is_locked():
2234
2465
            self._note_lock('r')
2235
2466
        # All-in-one needs to always unlock/lock.
2242
2473
            took_lock = False
2243
2474
        try:
2244
2475
            self.control_files.lock_read()
 
2476
            return LogicalLockResult(self.unlock)
2245
2477
        except:
2246
2478
            if took_lock:
2247
2479
                self.repository.unlock()
2416
2648
        return result
2417
2649
 
2418
2650
    def get_stacked_on_url(self):
2419
 
        raise errors.UnstackableBranchFormat(self._format, self.base)
 
2651
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2420
2652
 
2421
2653
    def set_push_location(self, location):
2422
2654
        """See Branch.set_push_location."""
2612
2844
        if _mod_revision.is_null(last_revision):
2613
2845
            return
2614
2846
        if last_revision not in self._lefthand_history(revision_id):
2615
 
            raise errors.AppendRevisionsOnlyViolation(self.base)
 
2847
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
2616
2848
 
2617
2849
    def _gen_revision_history(self):
2618
2850
        """Generate the revision history from last revision
2718
2950
        if branch_location is None:
2719
2951
            return Branch.reference_parent(self, file_id, path,
2720
2952
                                           possible_transports)
2721
 
        branch_location = urlutils.join(self.base, branch_location)
 
2953
        branch_location = urlutils.join(self.user_url, branch_location)
2722
2954
        return Branch.open(branch_location,
2723
2955
                           possible_transports=possible_transports)
2724
2956
 
2770
3002
        return stacked_url
2771
3003
 
2772
3004
    def _get_append_revisions_only(self):
2773
 
        value = self.get_config().get_user_option('append_revisions_only')
2774
 
        return value == 'True'
 
3005
        return self.get_config(
 
3006
            ).get_user_option_as_bool('append_revisions_only')
2775
3007
 
2776
3008
    @needs_write_lock
2777
3009
    def generate_revision_history(self, revision_id, last_rev=None,
2839
3071
    """
2840
3072
 
2841
3073
    def get_stacked_on_url(self):
2842
 
        raise errors.UnstackableBranchFormat(self._format, self.base)
 
3074
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2843
3075
 
2844
3076
 
2845
3077
######################################################################
2932
3164
        :param verbose: Requests more detailed display of what was checked,
2933
3165
            if any.
2934
3166
        """
2935
 
        note('checked branch %s format %s', self.branch.base,
 
3167
        note('checked branch %s format %s', self.branch.user_url,
2936
3168
            self.branch._format)
2937
3169
        for error in self.errors:
2938
3170
            note('found error:%s', error)
3033
3265
    _optimisers = []
3034
3266
    """The available optimised InterBranch types."""
3035
3267
 
3036
 
    @staticmethod
3037
 
    def _get_branch_formats_to_test():
3038
 
        """Return a tuple with the Branch formats to use when testing."""
3039
 
        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)
3040
3277
 
 
3278
    @needs_write_lock
3041
3279
    def pull(self, overwrite=False, stop_revision=None,
3042
3280
             possible_transports=None, local=False):
3043
3281
        """Mirror source into target branch.
3048
3286
        """
3049
3287
        raise NotImplementedError(self.pull)
3050
3288
 
 
3289
    @needs_write_lock
3051
3290
    def update_revisions(self, stop_revision=None, overwrite=False,
3052
3291
                         graph=None):
3053
3292
        """Pull in new perfect-fit revisions.
3061
3300
        """
3062
3301
        raise NotImplementedError(self.update_revisions)
3063
3302
 
 
3303
    @needs_write_lock
3064
3304
    def push(self, overwrite=False, stop_revision=None,
3065
3305
             _override_hook_source_branch=None):
3066
3306
        """Mirror the source branch into the target branch.
3071
3311
 
3072
3312
 
3073
3313
class GenericInterBranch(InterBranch):
3074
 
    """InterBranch implementation that uses public Branch functions.
3075
 
    """
3076
 
 
3077
 
    @staticmethod
3078
 
    def _get_branch_formats_to_test():
3079
 
        return BranchFormat._default_format, BranchFormat._default_format
3080
 
 
 
3314
    """InterBranch implementation that uses public Branch functions."""
 
3315
 
 
3316
    @classmethod
 
3317
    def is_compatible(klass, source, target):
 
3318
        # GenericBranch uses the public API, so always compatible
 
3319
        return True
 
3320
 
 
3321
    @classmethod
 
3322
    def _get_branch_formats_to_test(klass):
 
3323
        return [(BranchFormat._default_format, BranchFormat._default_format)]
 
3324
 
 
3325
    @classmethod
 
3326
    def unwrap_format(klass, format):
 
3327
        if isinstance(format, remote.RemoteBranchFormat):
 
3328
            format._ensure_real()
 
3329
            return format._custom_format
 
3330
        return format                                                                                                  
 
3331
 
 
3332
    @needs_write_lock
 
3333
    def copy_content_into(self, revision_id=None):
 
3334
        """Copy the content of source into target
 
3335
 
 
3336
        revision_id: if not None, the revision history in the new branch will
 
3337
                     be truncated to end with revision_id.
 
3338
        """
 
3339
        self.source.update_references(self.target)
 
3340
        self.source._synchronize_history(self.target, revision_id)
 
3341
        try:
 
3342
            parent = self.source.get_parent()
 
3343
        except errors.InaccessibleParent, e:
 
3344
            mutter('parent was not accessible to copy: %s', e)
 
3345
        else:
 
3346
            if parent:
 
3347
                self.target.set_parent(parent)
 
3348
        if self.source._push_should_merge_tags():
 
3349
            self.source.tags.merge_to(self.target.tags)
 
3350
 
 
3351
    @needs_write_lock
3081
3352
    def update_revisions(self, stop_revision=None, overwrite=False,
3082
3353
        graph=None):
3083
3354
        """See InterBranch.update_revisions()."""
3084
 
        self.source.lock_read()
3085
 
        try:
3086
 
            other_revno, other_last_revision = self.source.last_revision_info()
3087
 
            stop_revno = None # unknown
3088
 
            if stop_revision is None:
3089
 
                stop_revision = other_last_revision
3090
 
                if _mod_revision.is_null(stop_revision):
3091
 
                    # if there are no commits, we're done.
3092
 
                    return
3093
 
                stop_revno = other_revno
3094
 
 
3095
 
            # what's the current last revision, before we fetch [and change it
3096
 
            # possibly]
3097
 
            last_rev = _mod_revision.ensure_null(self.target.last_revision())
3098
 
            # we fetch here so that we don't process data twice in the common
3099
 
            # case of having something to pull, and so that the check for
3100
 
            # already merged can operate on the just fetched graph, which will
3101
 
            # be cached in memory.
3102
 
            self.target.fetch(self.source, stop_revision)
3103
 
            # Check to see if one is an ancestor of the other
3104
 
            if not overwrite:
3105
 
                if graph is None:
3106
 
                    graph = self.target.repository.get_graph()
3107
 
                if self.target._check_if_descendant_or_diverged(
3108
 
                        stop_revision, last_rev, graph, self.source):
3109
 
                    # stop_revision is a descendant of last_rev, but we aren't
3110
 
                    # overwriting, so we're done.
3111
 
                    return
3112
 
            if stop_revno is None:
3113
 
                if graph is None:
3114
 
                    graph = self.target.repository.get_graph()
3115
 
                this_revno, this_last_revision = \
3116
 
                        self.target.last_revision_info()
3117
 
                stop_revno = graph.find_distance_to_null(stop_revision,
3118
 
                                [(other_last_revision, other_revno),
3119
 
                                 (this_last_revision, this_revno)])
3120
 
            self.target.set_last_revision_info(stop_revno, stop_revision)
3121
 
        finally:
3122
 
            self.source.unlock()
3123
 
 
 
3355
        other_revno, other_last_revision = self.source.last_revision_info()
 
3356
        stop_revno = None # unknown
 
3357
        if stop_revision is None:
 
3358
            stop_revision = other_last_revision
 
3359
            if _mod_revision.is_null(stop_revision):
 
3360
                # if there are no commits, we're done.
 
3361
                return
 
3362
            stop_revno = other_revno
 
3363
 
 
3364
        # what's the current last revision, before we fetch [and change it
 
3365
        # possibly]
 
3366
        last_rev = _mod_revision.ensure_null(self.target.last_revision())
 
3367
        # we fetch here so that we don't process data twice in the common
 
3368
        # case of having something to pull, and so that the check for
 
3369
        # already merged can operate on the just fetched graph, which will
 
3370
        # be cached in memory.
 
3371
        self.target.fetch(self.source, stop_revision)
 
3372
        # Check to see if one is an ancestor of the other
 
3373
        if not overwrite:
 
3374
            if graph is None:
 
3375
                graph = self.target.repository.get_graph()
 
3376
            if self.target._check_if_descendant_or_diverged(
 
3377
                    stop_revision, last_rev, graph, self.source):
 
3378
                # stop_revision is a descendant of last_rev, but we aren't
 
3379
                # overwriting, so we're done.
 
3380
                return
 
3381
        if stop_revno is None:
 
3382
            if graph is None:
 
3383
                graph = self.target.repository.get_graph()
 
3384
            this_revno, this_last_revision = \
 
3385
                    self.target.last_revision_info()
 
3386
            stop_revno = graph.find_distance_to_null(stop_revision,
 
3387
                            [(other_last_revision, other_revno),
 
3388
                             (this_last_revision, this_revno)])
 
3389
        self.target.set_last_revision_info(stop_revno, stop_revision)
 
3390
 
 
3391
    @needs_write_lock
3124
3392
    def pull(self, overwrite=False, stop_revision=None,
3125
 
             possible_transports=None, _hook_master=None, run_hooks=True,
 
3393
             possible_transports=None, run_hooks=True,
3126
3394
             _override_hook_target=None, local=False):
3127
 
        """See Branch.pull.
 
3395
        """Pull from source into self, updating my master if any.
3128
3396
 
3129
 
        :param _hook_master: Private parameter - set the branch to
3130
 
            be supplied as the master to pull hooks.
3131
3397
        :param run_hooks: Private parameter - if false, this branch
3132
3398
            is being called because it's the master of the primary branch,
3133
3399
            so it should not run its hooks.
3134
 
        :param _override_hook_target: Private parameter - set the branch to be
3135
 
            supplied as the target_branch to pull hooks.
3136
 
        :param local: Only update the local branch, and not the bound branch.
3137
3400
        """
3138
 
        # This type of branch can't be bound.
3139
 
        if local:
 
3401
        bound_location = self.target.get_bound_location()
 
3402
        if local and not bound_location:
3140
3403
            raise errors.LocalRequiresBoundBranch()
3141
 
        result = PullResult()
3142
 
        result.source_branch = self.source
3143
 
        if _override_hook_target is None:
3144
 
            result.target_branch = self.target
3145
 
        else:
3146
 
            result.target_branch = _override_hook_target
3147
 
        self.source.lock_read()
 
3404
        master_branch = None
 
3405
        if not local and bound_location and self.source.user_url != bound_location:
 
3406
            # not pulling from master, so we need to update master.
 
3407
            master_branch = self.target.get_master_branch(possible_transports)
 
3408
            master_branch.lock_write()
3148
3409
        try:
3149
 
            # We assume that during 'pull' the target repository is closer than
3150
 
            # the source one.
3151
 
            self.source.update_references(self.target)
3152
 
            graph = self.target.repository.get_graph(self.source.repository)
3153
 
            # TODO: Branch formats should have a flag that indicates 
3154
 
            # that revno's are expensive, and pull() should honor that flag.
3155
 
            # -- JRV20090506
3156
 
            result.old_revno, result.old_revid = \
3157
 
                self.target.last_revision_info()
3158
 
            self.target.update_revisions(self.source, stop_revision,
3159
 
                overwrite=overwrite, graph=graph)
3160
 
            # TODO: The old revid should be specified when merging tags, 
3161
 
            # so a tags implementation that versions tags can only 
3162
 
            # pull in the most recent changes. -- JRV20090506
3163
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3164
 
                overwrite)
3165
 
            result.new_revno, result.new_revid = self.target.last_revision_info()
3166
 
            if _hook_master:
3167
 
                result.master_branch = _hook_master
3168
 
                result.local_branch = result.target_branch
3169
 
            else:
3170
 
                result.master_branch = result.target_branch
3171
 
                result.local_branch = None
3172
 
            if run_hooks:
3173
 
                for hook in Branch.hooks['post_pull']:
3174
 
                    hook(result)
 
3410
            if master_branch:
 
3411
                # pull from source into master.
 
3412
                master_branch.pull(self.source, overwrite, stop_revision,
 
3413
                    run_hooks=False)
 
3414
            return self._pull(overwrite,
 
3415
                stop_revision, _hook_master=master_branch,
 
3416
                run_hooks=run_hooks,
 
3417
                _override_hook_target=_override_hook_target)
3175
3418
        finally:
3176
 
            self.source.unlock()
3177
 
        return result
 
3419
            if master_branch:
 
3420
                master_branch.unlock()
3178
3421
 
3179
3422
    def push(self, overwrite=False, stop_revision=None,
3180
3423
             _override_hook_source_branch=None):
3242
3485
            _run_hooks()
3243
3486
            return result
3244
3487
 
3245
 
    @classmethod
3246
 
    def is_compatible(self, source, target):
3247
 
        # GenericBranch uses the public API, so always compatible
3248
 
        return True
3249
 
 
3250
 
 
3251
 
class InterToBranch5(GenericInterBranch):
3252
 
 
3253
 
    @staticmethod
3254
 
    def _get_branch_formats_to_test():
3255
 
        return BranchFormat._default_format, BzrBranchFormat5()
3256
 
 
3257
 
    def pull(self, overwrite=False, stop_revision=None,
3258
 
             possible_transports=None, run_hooks=True,
 
3488
    def _pull(self, overwrite=False, stop_revision=None,
 
3489
             possible_transports=None, _hook_master=None, run_hooks=True,
3259
3490
             _override_hook_target=None, local=False):
3260
 
        """Pull from source into self, updating my master if any.
3261
 
 
 
3491
        """See Branch.pull.
 
3492
 
 
3493
        This function is the core worker, used by GenericInterBranch.pull to
 
3494
        avoid duplication when pulling source->master and source->local.
 
3495
 
 
3496
        :param _hook_master: Private parameter - set the branch to
 
3497
            be supplied as the master to pull hooks.
3262
3498
        :param run_hooks: Private parameter - if false, this branch
3263
3499
            is being called because it's the master of the primary branch,
3264
3500
            so it should not run its hooks.
 
3501
        :param _override_hook_target: Private parameter - set the branch to be
 
3502
            supplied as the target_branch to pull hooks.
 
3503
        :param local: Only update the local branch, and not the bound branch.
3265
3504
        """
3266
 
        bound_location = self.target.get_bound_location()
3267
 
        if local and not bound_location:
 
3505
        # This type of branch can't be bound.
 
3506
        if local:
3268
3507
            raise errors.LocalRequiresBoundBranch()
3269
 
        master_branch = None
3270
 
        if not local and bound_location and self.source.base != bound_location:
3271
 
            # not pulling from master, so we need to update master.
3272
 
            master_branch = self.target.get_master_branch(possible_transports)
3273
 
            master_branch.lock_write()
 
3508
        result = PullResult()
 
3509
        result.source_branch = self.source
 
3510
        if _override_hook_target is None:
 
3511
            result.target_branch = self.target
 
3512
        else:
 
3513
            result.target_branch = _override_hook_target
 
3514
        self.source.lock_read()
3274
3515
        try:
3275
 
            if master_branch:
3276
 
                # pull from source into master.
3277
 
                master_branch.pull(self.source, overwrite, stop_revision,
3278
 
                    run_hooks=False)
3279
 
            return super(InterToBranch5, self).pull(overwrite,
3280
 
                stop_revision, _hook_master=master_branch,
3281
 
                run_hooks=run_hooks,
3282
 
                _override_hook_target=_override_hook_target)
 
3516
            # We assume that during 'pull' the target repository is closer than
 
3517
            # the source one.
 
3518
            self.source.update_references(self.target)
 
3519
            graph = self.target.repository.get_graph(self.source.repository)
 
3520
            # TODO: Branch formats should have a flag that indicates 
 
3521
            # that revno's are expensive, and pull() should honor that flag.
 
3522
            # -- JRV20090506
 
3523
            result.old_revno, result.old_revid = \
 
3524
                self.target.last_revision_info()
 
3525
            self.target.update_revisions(self.source, stop_revision,
 
3526
                overwrite=overwrite, graph=graph)
 
3527
            # TODO: The old revid should be specified when merging tags, 
 
3528
            # so a tags implementation that versions tags can only 
 
3529
            # pull in the most recent changes. -- JRV20090506
 
3530
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3531
                overwrite)
 
3532
            result.new_revno, result.new_revid = self.target.last_revision_info()
 
3533
            if _hook_master:
 
3534
                result.master_branch = _hook_master
 
3535
                result.local_branch = result.target_branch
 
3536
            else:
 
3537
                result.master_branch = result.target_branch
 
3538
                result.local_branch = None
 
3539
            if run_hooks:
 
3540
                for hook in Branch.hooks['post_pull']:
 
3541
                    hook(result)
3283
3542
        finally:
3284
 
            if master_branch:
3285
 
                master_branch.unlock()
 
3543
            self.source.unlock()
 
3544
        return result
3286
3545
 
3287
3546
 
3288
3547
InterBranch.register_optimiser(GenericInterBranch)
3289
 
InterBranch.register_optimiser(InterToBranch5)