~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Andrew Bennetts
  • Date: 2010-10-08 04:25:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5472.
  • Revision ID: andrew.bennetts@canonical.com-20101008042510-sg9vdhmnggilzxsk
Fix stray TAB in source.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
25
25
        bzrdir,
26
26
        cache_utf8,
27
27
        config as _mod_config,
 
28
        controldir,
28
29
        debug,
29
30
        errors,
30
 
        fetch,
31
 
        graph as _mod_graph,
32
31
        lockdir,
33
32
        lockable_files,
34
33
        remote,
42
41
        urlutils,
43
42
        )
44
43
from bzrlib.config import BranchConfig, TransportConfig
 
44
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
45
45
from bzrlib.tag import (
46
46
    BasicTags,
47
47
    DisabledTags,
48
48
    )
49
49
""")
50
50
 
51
 
from bzrlib import (
52
 
    controldir,
53
 
    )
54
 
from bzrlib.decorators import (
55
 
    needs_read_lock,
56
 
    needs_write_lock,
57
 
    only_raises,
58
 
    )
59
 
from bzrlib.hooks import Hooks
 
51
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
 
52
from bzrlib.hooks import HookPoint, Hooks
60
53
from bzrlib.inter import InterObject
61
54
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
62
55
from bzrlib import registry
78
71
    :ivar base:
79
72
        Base directory/url of the branch; using control_url and
80
73
        control_transport is more standardized.
81
 
    :ivar hooks: An instance of BranchHooks.
82
 
    :ivar _master_branch_cache: cached result of get_master_branch, see
83
 
        _clear_cached_state.
 
74
 
 
75
    hooks: An instance of BranchHooks.
84
76
    """
85
77
    # this is really an instance variable - FIXME move it there
86
78
    # - RBC 20060112
100
92
        self._revision_id_to_revno_cache = None
101
93
        self._partial_revision_id_to_revno_cache = {}
102
94
        self._partial_revision_history_cache = []
103
 
        self._tags_bytes = None
104
95
        self._last_revision_info_cache = None
105
 
        self._master_branch_cache = None
106
96
        self._merge_sorted_revisions_cache = None
107
97
        self._open_hook()
108
98
        hooks = Branch.hooks['open']
114
104
 
115
105
    def _activate_fallback_location(self, url):
116
106
        """Activate the branch/repository from url as a fallback repository."""
117
 
        for existing_fallback_repo in self.repository._fallback_repositories:
118
 
            if existing_fallback_repo.user_url == url:
119
 
                # This fallback is already configured.  This probably only
120
 
                # happens because BzrDir.sprout is a horrible mess.  To avoid
121
 
                # confusing _unstack we don't add this a second time.
122
 
                mutter('duplicate activation of fallback %r on %r', url, self)
123
 
                return
124
107
        repo = self._get_fallback_repository(url)
125
108
        if repo.has_same_location(self.repository):
126
109
            raise errors.UnstackableLocationError(self.user_url, url)
244
227
            possible_transports=[self.bzrdir.root_transport])
245
228
        return a_branch.repository
246
229
 
247
 
    @needs_read_lock
248
230
    def _get_tags_bytes(self):
249
231
        """Get the bytes of a serialised tags dict.
250
232
 
257
239
        :return: The bytes of the tags file.
258
240
        :seealso: Branch._set_tags_bytes.
259
241
        """
260
 
        if self._tags_bytes is None:
261
 
            self._tags_bytes = self._transport.get_bytes('tags')
262
 
        return self._tags_bytes
 
242
        return self._transport.get_bytes('tags')
263
243
 
264
244
    def _get_nick(self, local=False, possible_transports=None):
265
245
        config = self.get_config()
669
649
        raise errors.UnsupportedOperation(self.get_reference_info, self)
670
650
 
671
651
    @needs_write_lock
672
 
    def fetch(self, from_branch, last_revision=None):
 
652
    def fetch(self, from_branch, last_revision=None, pb=None):
673
653
        """Copy revisions from from_branch into this branch.
674
654
 
675
655
        :param from_branch: Where to copy from.
676
656
        :param last_revision: What revision to stop at (None for at the end
677
657
                              of the branch.
 
658
        :param pb: An optional progress bar to use.
678
659
        :return: None
679
660
        """
680
 
        return InterBranch.get(from_branch, self).fetch(last_revision)
 
661
        if self.base == from_branch.base:
 
662
            return (0, [])
 
663
        if pb is not None:
 
664
            symbol_versioning.warn(
 
665
                symbol_versioning.deprecated_in((1, 14, 0))
 
666
                % "pb parameter to fetch()")
 
667
        from_branch.lock_read()
 
668
        try:
 
669
            if last_revision is None:
 
670
                last_revision = from_branch.last_revision()
 
671
                last_revision = _mod_revision.ensure_null(last_revision)
 
672
            return self.repository.fetch(from_branch.repository,
 
673
                                         revision_id=last_revision,
 
674
                                         pb=pb)
 
675
        finally:
 
676
            from_branch.unlock()
681
677
 
682
678
    def get_bound_location(self):
683
679
        """Return the URL of the branch we are bound to.
694
690
 
695
691
    def get_commit_builder(self, parents, config=None, timestamp=None,
696
692
                           timezone=None, committer=None, revprops=None,
697
 
                           revision_id=None, lossy=False):
 
693
                           revision_id=None):
698
694
        """Obtain a CommitBuilder for this branch.
699
695
 
700
696
        :param parents: Revision ids of the parents of the new revision.
704
700
        :param committer: Optional committer to set for commit.
705
701
        :param revprops: Optional dictionary of revision properties.
706
702
        :param revision_id: Optional revision id.
707
 
        :param lossy: Whether to discard data that can not be natively
708
 
            represented, when pushing to a foreign VCS 
709
703
        """
710
704
 
711
705
        if config is None:
712
706
            config = self.get_config()
713
707
 
714
708
        return self.repository.get_commit_builder(self, parents, config,
715
 
            timestamp, timezone, committer, revprops, revision_id,
716
 
            lossy)
 
709
            timestamp, timezone, committer, revprops, revision_id)
717
710
 
718
711
    def get_master_branch(self, possible_transports=None):
719
712
        """Return the branch we are bound to.
797
790
 
798
791
    def _unstack(self):
799
792
        """Change a branch to be unstacked, copying data as needed.
800
 
 
 
793
        
801
794
        Don't call this directly, use set_stacked_on_url(None).
802
795
        """
803
796
        pb = ui.ui_factory.nested_progress_bar()
812
805
            old_repository = self.repository
813
806
            if len(old_repository._fallback_repositories) != 1:
814
807
                raise AssertionError("can't cope with fallback repositories "
815
 
                    "of %r (fallbacks: %r)" % (old_repository,
816
 
                        old_repository._fallback_repositories))
 
808
                    "of %r" % (self.repository,))
817
809
            # Open the new repository object.
818
810
            # Repositories don't offer an interface to remove fallback
819
811
            # repositories today; take the conceptually simpler option and just
867
859
                # XXX: If you unstack a branch while it has a working tree
868
860
                # with a pending merge, the pending-merged revisions will no
869
861
                # longer be present.  You can (probably) revert and remerge.
870
 
                try:
871
 
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
872
 
                except errors.TagsNotSupported:
873
 
                    tags_to_fetch = set()
874
 
                fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
875
 
                    old_repository, required_ids=[self.last_revision()],
876
 
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
877
 
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
 
862
                #
 
863
                # XXX: This only fetches up to the tip of the repository; it
 
864
                # doesn't bring across any tags.  That's fairly consistent
 
865
                # with how branch works, but perhaps not ideal.
 
866
                self.repository.fetch(old_repository,
 
867
                    revision_id=self.last_revision(),
 
868
                    find_ghosts=True)
878
869
            finally:
879
870
                old_repository.unlock()
880
871
        finally:
885
876
 
886
877
        :seealso: Branch._get_tags_bytes.
887
878
        """
888
 
        return _run_with_write_locked_target(self, self._set_tags_bytes_locked,
889
 
                bytes)
890
 
 
891
 
    def _set_tags_bytes_locked(self, bytes):
892
 
        self._tags_bytes = bytes
893
 
        return self._transport.put_bytes('tags', bytes)
 
879
        return _run_with_write_locked_target(self, self._transport.put_bytes,
 
880
            'tags', bytes)
894
881
 
895
882
    def _cache_revision_history(self, rev_history):
896
883
        """Set the cached revision history to rev_history.
923
910
        self._revision_history_cache = None
924
911
        self._revision_id_to_revno_cache = None
925
912
        self._last_revision_info_cache = None
926
 
        self._master_branch_cache = None
927
913
        self._merge_sorted_revisions_cache = None
928
914
        self._partial_revision_history_cache = []
929
915
        self._partial_revision_id_to_revno_cache = {}
930
 
        self._tags_bytes = None
931
916
 
932
917
    def _gen_revision_history(self):
933
918
        """Return sequence of revision hashes on to this branch.
994
979
        else:
995
980
            return (0, _mod_revision.NULL_REVISION)
996
981
 
997
 
    @deprecated_method(deprecated_in((2, 4, 0)))
 
982
    @deprecated_method(deprecated_in((1, 6, 0)))
 
983
    def missing_revisions(self, other, stop_revision=None):
 
984
        """Return a list of new revisions that would perfectly fit.
 
985
 
 
986
        If self and other have not diverged, return a list of the revisions
 
987
        present in other, but missing from self.
 
988
        """
 
989
        self_history = self.revision_history()
 
990
        self_len = len(self_history)
 
991
        other_history = other.revision_history()
 
992
        other_len = len(other_history)
 
993
        common_index = min(self_len, other_len) -1
 
994
        if common_index >= 0 and \
 
995
            self_history[common_index] != other_history[common_index]:
 
996
            raise errors.DivergedBranches(self, other)
 
997
 
 
998
        if stop_revision is None:
 
999
            stop_revision = other_len
 
1000
        else:
 
1001
            if stop_revision > other_len:
 
1002
                raise errors.NoSuchRevision(self, stop_revision)
 
1003
        return other_history[self_len:stop_revision]
 
1004
 
 
1005
    def update_revisions(self, other, stop_revision=None, overwrite=False,
 
1006
                         graph=None):
 
1007
        """Pull in new perfect-fit revisions.
 
1008
 
 
1009
        :param other: Another Branch to pull from
 
1010
        :param stop_revision: Updated until the given revision
 
1011
        :param overwrite: Always set the branch pointer, rather than checking
 
1012
            to see if it is a proper descendant.
 
1013
        :param graph: A Graph object that can be used to query history
 
1014
            information. This can be None.
 
1015
        :return: None
 
1016
        """
 
1017
        return InterBranch.get(other, self).update_revisions(stop_revision,
 
1018
            overwrite, graph)
 
1019
 
998
1020
    def import_last_revision_info(self, source_repo, revno, revid):
999
1021
        """Set the last revision info, importing from another repo if necessary.
1000
1022
 
 
1023
        This is used by the bound branch code to upload a revision to
 
1024
        the master branch first before updating the tip of the local branch.
 
1025
 
1001
1026
        :param source_repo: Source repository to optionally fetch from
1002
1027
        :param revno: Revision number of the new tip
1003
1028
        :param revid: Revision id of the new tip
1006
1031
            self.repository.fetch(source_repo, revision_id=revid)
1007
1032
        self.set_last_revision_info(revno, revid)
1008
1033
 
1009
 
    def import_last_revision_info_and_tags(self, source, revno, revid,
1010
 
                                           lossy=False):
1011
 
        """Set the last revision info, importing from another repo if necessary.
1012
 
 
1013
 
        This is used by the bound branch code to upload a revision to
1014
 
        the master branch first before updating the tip of the local branch.
1015
 
        Revisions referenced by source's tags are also transferred.
1016
 
 
1017
 
        :param source: Source branch to optionally fetch from
1018
 
        :param revno: Revision number of the new tip
1019
 
        :param revid: Revision id of the new tip
1020
 
        :param lossy: Whether to discard metadata that can not be
1021
 
            natively represented
1022
 
        :return: Tuple with the new revision number and revision id
1023
 
            (should only be different from the arguments when lossy=True)
1024
 
        """
1025
 
        if not self.repository.has_same_location(source.repository):
1026
 
            self.fetch(source, revid)
1027
 
        self.set_last_revision_info(revno, revid)
1028
 
        return (revno, revid)
1029
 
 
1030
1034
    def revision_id_to_revno(self, revision_id):
1031
1035
        """Given a revision id, return its revno"""
1032
1036
        if _mod_revision.is_null(revision_id):
1253
1257
        return result
1254
1258
 
1255
1259
    @needs_read_lock
1256
 
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
1257
 
            repository=None):
 
1260
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
1258
1261
        """Create a new line of development from the branch, into to_bzrdir.
1259
1262
 
1260
1263
        to_bzrdir controls the branch format.
1265
1268
        if (repository_policy is not None and
1266
1269
            repository_policy.requires_stacking()):
1267
1270
            to_bzrdir._format.require_stacking(_skip_repo=True)
1268
 
        result = to_bzrdir.create_branch(repository=repository)
 
1271
        result = to_bzrdir.create_branch()
1269
1272
        result.lock_write()
1270
1273
        try:
1271
1274
            if repository_policy is not None:
1359
1362
        """Return the most suitable metadir for a checkout of this branch.
1360
1363
        Weaves are used if this branch's repository uses weaves.
1361
1364
        """
1362
 
        format = self.repository.bzrdir.checkout_metadir()
1363
 
        format.set_branch_format(self._format)
 
1365
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
 
1366
            from bzrlib.repofmt import weaverepo
 
1367
            format = bzrdir.BzrDirMetaFormat1()
 
1368
            format.repository_format = weaverepo.RepositoryFormat7()
 
1369
        else:
 
1370
            format = self.repository.bzrdir.checkout_metadir()
 
1371
            format.set_branch_format(self._format)
1364
1372
        return format
1365
1373
 
1366
1374
    def create_clone_on_transport(self, to_transport, revision_id=None,
1367
 
        stacked_on=None, create_prefix=False, use_existing_dir=False,
1368
 
        no_tree=None):
 
1375
        stacked_on=None, create_prefix=False, use_existing_dir=False):
1369
1376
        """Create a clone of this branch and its bzrdir.
1370
1377
 
1371
1378
        :param to_transport: The transport to clone onto.
1384
1391
            revision_id = self.last_revision()
1385
1392
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1386
1393
            revision_id=revision_id, stacked_on=stacked_on,
1387
 
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
1388
 
            no_tree=no_tree)
 
1394
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1389
1395
        return dir_to.open_branch()
1390
1396
 
1391
1397
    def create_checkout(self, to_location, revision_id=None,
1506
1512
        else:
1507
1513
            raise AssertionError("invalid heads: %r" % (heads,))
1508
1514
 
1509
 
    def heads_to_fetch(self):
1510
 
        """Return the heads that must and that should be fetched to copy this
1511
 
        branch into another repo.
1512
 
 
1513
 
        :returns: a 2-tuple of (must_fetch, if_present_fetch).  must_fetch is a
1514
 
            set of heads that must be fetched.  if_present_fetch is a set of
1515
 
            heads that must be fetched if present, but no error is necessary if
1516
 
            they are not present.
1517
 
        """
1518
 
        # For bzr native formats must_fetch is just the tip, and if_present_fetch
1519
 
        # are the tags.
1520
 
        must_fetch = set([self.last_revision()])
1521
 
        try:
1522
 
            if_present_fetch = set(self.tags.get_reverse_tag_dict())
1523
 
        except errors.TagsNotSupported:
1524
 
            if_present_fetch = set()
1525
 
        must_fetch.discard(_mod_revision.NULL_REVISION)
1526
 
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
1527
 
        return must_fetch, if_present_fetch
1528
 
 
1529
 
 
1530
 
class BranchFormat(controldir.ControlComponentFormat):
 
1515
 
 
1516
class BranchFormat(object):
1531
1517
    """An encapsulation of the initialization and open routines for a format.
1532
1518
 
1533
1519
    Formats provide three things:
1545
1531
    object will be created every time regardless.
1546
1532
    """
1547
1533
 
 
1534
    _default_format = None
 
1535
    """The default format used for new branches."""
 
1536
 
 
1537
    _formats = {}
 
1538
    """The known formats."""
 
1539
 
1548
1540
    can_set_append_revisions_only = True
1549
1541
 
1550
1542
    def __eq__(self, other):
1559
1551
        try:
1560
1552
            transport = a_bzrdir.get_branch_transport(None, name=name)
1561
1553
            format_string = transport.get_bytes("format")
1562
 
            return format_registry.get(format_string)
 
1554
            format = klass._formats[format_string]
 
1555
            if isinstance(format, MetaDirBranchFormatFactory):
 
1556
                return format()
 
1557
            return format
1563
1558
        except errors.NoSuchFile:
1564
1559
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1565
1560
        except KeyError:
1566
1561
            raise errors.UnknownFormatError(format=format_string, kind='branch')
1567
1562
 
1568
1563
    @classmethod
1569
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1570
1564
    def get_default_format(klass):
1571
1565
        """Return the current default format."""
1572
 
        return format_registry.get_default()
 
1566
        return klass._default_format
1573
1567
 
1574
1568
    @classmethod
1575
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1576
1569
    def get_formats(klass):
1577
1570
        """Get all the known formats.
1578
1571
 
1579
1572
        Warning: This triggers a load of all lazy registered formats: do not
1580
1573
        use except when that is desireed.
1581
1574
        """
1582
 
        return format_registry._get_all()
 
1575
        result = []
 
1576
        for fmt in klass._formats.values():
 
1577
            if isinstance(fmt, MetaDirBranchFormatFactory):
 
1578
                fmt = fmt()
 
1579
            result.append(fmt)
 
1580
        return result
1583
1581
 
1584
1582
    def get_reference(self, a_bzrdir, name=None):
1585
1583
        """Get the target reference of the branch in a_bzrdir.
1624
1622
        for hook in hooks:
1625
1623
            hook(params)
1626
1624
 
1627
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
1625
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
1626
                           lock_type='metadir', set_format=True):
 
1627
        """Initialize a branch in a bzrdir, with specified files
 
1628
 
 
1629
        :param a_bzrdir: The bzrdir to initialize the branch in
 
1630
        :param utf8_files: The files to create as a list of
 
1631
            (filename, content) tuples
 
1632
        :param name: Name of colocated branch to create, if any
 
1633
        :param set_format: If True, set the format with
 
1634
            self.get_format_string.  (BzrBranch4 has its format set
 
1635
            elsewhere)
 
1636
        :return: a branch in this format
 
1637
        """
 
1638
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
1639
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
 
1640
        lock_map = {
 
1641
            'metadir': ('lock', lockdir.LockDir),
 
1642
            'branch4': ('branch-lock', lockable_files.TransportLock),
 
1643
        }
 
1644
        lock_name, lock_class = lock_map[lock_type]
 
1645
        control_files = lockable_files.LockableFiles(branch_transport,
 
1646
            lock_name, lock_class)
 
1647
        control_files.create_lock()
 
1648
        try:
 
1649
            control_files.lock_write()
 
1650
        except errors.LockContention:
 
1651
            if lock_type != 'branch4':
 
1652
                raise
 
1653
            lock_taken = False
 
1654
        else:
 
1655
            lock_taken = True
 
1656
        if set_format:
 
1657
            utf8_files += [('format', self.get_format_string())]
 
1658
        try:
 
1659
            for (filename, content) in utf8_files:
 
1660
                branch_transport.put_bytes(
 
1661
                    filename, content,
 
1662
                    mode=a_bzrdir._get_file_mode())
 
1663
        finally:
 
1664
            if lock_taken:
 
1665
                control_files.unlock()
 
1666
        branch = self.open(a_bzrdir, name, _found=True)
 
1667
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
1668
        return branch
 
1669
 
 
1670
    def initialize(self, a_bzrdir, name=None):
1628
1671
        """Create a branch of this format in a_bzrdir.
1629
1672
        
1630
1673
        :param name: Name of the colocated branch to create.
1664
1707
        """
1665
1708
        raise NotImplementedError(self.network_name)
1666
1709
 
1667
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
1668
 
            found_repository=None):
 
1710
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1669
1711
        """Return the branch object for a_bzrdir
1670
1712
 
1671
1713
        :param a_bzrdir: A BzrDir that contains a branch.
1678
1720
        raise NotImplementedError(self.open)
1679
1721
 
1680
1722
    @classmethod
1681
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1682
1723
    def register_format(klass, format):
1683
1724
        """Register a metadir format.
1684
 
 
 
1725
        
1685
1726
        See MetaDirBranchFormatFactory for the ability to register a format
1686
1727
        without loading the code the format needs until it is actually used.
1687
1728
        """
1688
 
        format_registry.register(format)
 
1729
        klass._formats[format.get_format_string()] = format
 
1730
        # Metadir formats have a network name of their format string, and get
 
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__)
1689
1737
 
1690
1738
    @classmethod
1691
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1692
1739
    def set_default_format(klass, format):
1693
 
        format_registry.set_default(format)
 
1740
        klass._default_format = format
1694
1741
 
1695
1742
    def supports_set_append_revisions_only(self):
1696
1743
        """True if this format supports set_append_revisions_only."""
1700
1747
        """True if this format records a stacked-on branch."""
1701
1748
        return False
1702
1749
 
1703
 
    def supports_leaving_lock(self):
1704
 
        """True if this format supports leaving locks in place."""
1705
 
        return False # by default
1706
 
 
1707
1750
    @classmethod
1708
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1709
1751
    def unregister_format(klass, format):
1710
 
        format_registry.remove(format)
 
1752
        del klass._formats[format.get_format_string()]
1711
1753
 
1712
1754
    def __str__(self):
1713
1755
        return self.get_format_description().rstrip()
1758
1800
        These are all empty initially, because by default nothing should get
1759
1801
        notified.
1760
1802
        """
1761
 
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1762
 
        self.add_hook('set_rh',
 
1803
        Hooks.__init__(self)
 
1804
        self.create_hook(HookPoint('set_rh',
1763
1805
            "Invoked whenever the revision history has been set via "
1764
1806
            "set_revision_history. The api signature is (branch, "
1765
1807
            "revision_history), and the branch will be write-locked. "
1766
1808
            "The set_rh hook can be expensive for bzr to trigger, a better "
1767
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
1768
 
        self.add_hook('open',
 
1809
            "hook to use is Branch.post_change_branch_tip.", (0, 15), None))
 
1810
        self.create_hook(HookPoint('open',
1769
1811
            "Called with the Branch object that has been opened after a "
1770
 
            "branch is opened.", (1, 8))
1771
 
        self.add_hook('post_push',
 
1812
            "branch is opened.", (1, 8), None))
 
1813
        self.create_hook(HookPoint('post_push',
1772
1814
            "Called after a push operation completes. post_push is called "
1773
1815
            "with a bzrlib.branch.BranchPushResult object and only runs in the "
1774
 
            "bzr client.", (0, 15))
1775
 
        self.add_hook('post_pull',
 
1816
            "bzr client.", (0, 15), None))
 
1817
        self.create_hook(HookPoint('post_pull',
1776
1818
            "Called after a pull operation completes. post_pull is called "
1777
1819
            "with a bzrlib.branch.PullResult object and only runs in the "
1778
 
            "bzr client.", (0, 15))
1779
 
        self.add_hook('pre_commit',
 
1820
            "bzr client.", (0, 15), None))
 
1821
        self.create_hook(HookPoint('pre_commit',
1780
1822
            "Called after a commit is calculated but before it is "
1781
1823
            "completed. pre_commit is called with (local, master, old_revno, "
1782
1824
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1785
1827
            "basis revision. hooks MUST NOT modify this delta. "
1786
1828
            " future_tree is an in-memory tree obtained from "
1787
1829
            "CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1788
 
            "tree.", (0,91))
1789
 
        self.add_hook('post_commit',
 
1830
            "tree.", (0,91), None))
 
1831
        self.create_hook(HookPoint('post_commit',
1790
1832
            "Called in the bzr client after a commit has completed. "
1791
1833
            "post_commit is called with (local, master, old_revno, old_revid, "
1792
1834
            "new_revno, new_revid). old_revid is NULL_REVISION for the first "
1793
 
            "commit to a branch.", (0, 15))
1794
 
        self.add_hook('post_uncommit',
 
1835
            "commit to a branch.", (0, 15), None))
 
1836
        self.create_hook(HookPoint('post_uncommit',
1795
1837
            "Called in the bzr client after an uncommit completes. "
1796
1838
            "post_uncommit is called with (local, master, old_revno, "
1797
1839
            "old_revid, new_revno, new_revid) where local is the local branch "
1798
1840
            "or None, master is the target branch, and an empty branch "
1799
 
            "receives new_revno of 0, new_revid of None.", (0, 15))
1800
 
        self.add_hook('pre_change_branch_tip',
 
1841
            "receives new_revno of 0, new_revid of None.", (0, 15), None))
 
1842
        self.create_hook(HookPoint('pre_change_branch_tip',
1801
1843
            "Called in bzr client and server before a change to the tip of a "
1802
1844
            "branch is made. pre_change_branch_tip is called with a "
1803
1845
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1804
 
            "commit, uncommit will all trigger this hook.", (1, 6))
1805
 
        self.add_hook('post_change_branch_tip',
 
1846
            "commit, uncommit will all trigger this hook.", (1, 6), None))
 
1847
        self.create_hook(HookPoint('post_change_branch_tip',
1806
1848
            "Called in bzr client and server after a change to the tip of a "
1807
1849
            "branch is made. post_change_branch_tip is called with a "
1808
1850
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1809
 
            "commit, uncommit will all trigger this hook.", (1, 4))
1810
 
        self.add_hook('transform_fallback_location',
 
1851
            "commit, uncommit will all trigger this hook.", (1, 4), None))
 
1852
        self.create_hook(HookPoint('transform_fallback_location',
1811
1853
            "Called when a stacked branch is activating its fallback "
1812
1854
            "locations. transform_fallback_location is called with (branch, "
1813
1855
            "url), and should return a new url. Returning the same url "
1818
1860
            "fallback locations have not been activated. When there are "
1819
1861
            "multiple hooks installed for transform_fallback_location, "
1820
1862
            "all are called with the url returned from the previous hook."
1821
 
            "The order is however undefined.", (1, 9))
1822
 
        self.add_hook('automatic_tag_name',
 
1863
            "The order is however undefined.", (1, 9), None))
 
1864
        self.create_hook(HookPoint('automatic_tag_name',
1823
1865
            "Called to determine an automatic tag name for a revision. "
1824
1866
            "automatic_tag_name is called with (branch, revision_id) and "
1825
1867
            "should return a tag name or None if no tag name could be "
1826
1868
            "determined. The first non-None tag name returned will be used.",
1827
 
            (2, 2))
1828
 
        self.add_hook('post_branch_init',
 
1869
            (2, 2), None))
 
1870
        self.create_hook(HookPoint('post_branch_init',
1829
1871
            "Called after new branch initialization completes. "
1830
1872
            "post_branch_init is called with a "
1831
1873
            "bzrlib.branch.BranchInitHookParams. "
1832
1874
            "Note that init, branch and checkout (both heavyweight and "
1833
 
            "lightweight) will all trigger this hook.", (2, 2))
1834
 
        self.add_hook('post_switch',
 
1875
            "lightweight) will all trigger this hook.", (2, 2), None))
 
1876
        self.create_hook(HookPoint('post_switch',
1835
1877
            "Called after a checkout switches branch. "
1836
1878
            "post_switch is called with a "
1837
 
            "bzrlib.branch.SwitchHookParams.", (2, 2))
 
1879
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
1838
1880
 
1839
1881
 
1840
1882
 
1953
1995
            self.revision_id)
1954
1996
 
1955
1997
 
 
1998
class BzrBranchFormat4(BranchFormat):
 
1999
    """Bzr branch format 4.
 
2000
 
 
2001
    This format has:
 
2002
     - a revision-history file.
 
2003
     - a branch-lock lock file [ to be shared with the bzrdir ]
 
2004
    """
 
2005
 
 
2006
    def get_format_description(self):
 
2007
        """See BranchFormat.get_format_description()."""
 
2008
        return "Branch format 4"
 
2009
 
 
2010
    def initialize(self, a_bzrdir, name=None):
 
2011
        """Create a branch of this format in a_bzrdir."""
 
2012
        utf8_files = [('revision-history', ''),
 
2013
                      ('branch-name', ''),
 
2014
                      ]
 
2015
        return self._initialize_helper(a_bzrdir, utf8_files, name=name,
 
2016
                                       lock_type='branch4', set_format=False)
 
2017
 
 
2018
    def __init__(self):
 
2019
        super(BzrBranchFormat4, self).__init__()
 
2020
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
 
2021
 
 
2022
    def network_name(self):
 
2023
        """The network name for this format is the control dirs disk label."""
 
2024
        return self._matchingbzrdir.get_format_string()
 
2025
 
 
2026
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
2027
        """See BranchFormat.open()."""
 
2028
        if not _found:
 
2029
            # we are being called directly and must probe.
 
2030
            raise NotImplementedError
 
2031
        return BzrBranch(_format=self,
 
2032
                         _control_files=a_bzrdir._control_files,
 
2033
                         a_bzrdir=a_bzrdir,
 
2034
                         name=name,
 
2035
                         _repository=a_bzrdir.open_repository())
 
2036
 
 
2037
    def __str__(self):
 
2038
        return "Bazaar-NG branch format 4"
 
2039
 
 
2040
 
1956
2041
class BranchFormatMetadir(BranchFormat):
1957
2042
    """Common logic for meta-dir based branch formats."""
1958
2043
 
1960
2045
        """What class to instantiate on open calls."""
1961
2046
        raise NotImplementedError(self._branch_class)
1962
2047
 
1963
 
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1964
 
                           repository=None):
1965
 
        """Initialize a branch in a bzrdir, with specified files
1966
 
 
1967
 
        :param a_bzrdir: The bzrdir to initialize the branch in
1968
 
        :param utf8_files: The files to create as a list of
1969
 
            (filename, content) tuples
1970
 
        :param name: Name of colocated branch to create, if any
1971
 
        :return: a branch in this format
1972
 
        """
1973
 
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1974
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1975
 
        control_files = lockable_files.LockableFiles(branch_transport,
1976
 
            'lock', lockdir.LockDir)
1977
 
        control_files.create_lock()
1978
 
        control_files.lock_write()
1979
 
        try:
1980
 
            utf8_files += [('format', self.get_format_string())]
1981
 
            for (filename, content) in utf8_files:
1982
 
                branch_transport.put_bytes(
1983
 
                    filename, content,
1984
 
                    mode=a_bzrdir._get_file_mode())
1985
 
        finally:
1986
 
            control_files.unlock()
1987
 
        branch = self.open(a_bzrdir, name, _found=True,
1988
 
                found_repository=repository)
1989
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1990
 
        return branch
1991
 
 
1992
2048
    def network_name(self):
1993
2049
        """A simple byte string uniquely identifying this format for RPC calls.
1994
2050
 
1996
2052
        """
1997
2053
        return self.get_format_string()
1998
2054
 
1999
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2000
 
            found_repository=None):
 
2055
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2001
2056
        """See BranchFormat.open()."""
2002
2057
        if not _found:
2003
2058
            format = BranchFormat.find_format(a_bzrdir, name=name)
2008
2063
        try:
2009
2064
            control_files = lockable_files.LockableFiles(transport, 'lock',
2010
2065
                                                         lockdir.LockDir)
2011
 
            if found_repository is None:
2012
 
                found_repository = a_bzrdir.find_repository()
2013
2066
            return self._branch_class()(_format=self,
2014
2067
                              _control_files=control_files,
2015
2068
                              name=name,
2016
2069
                              a_bzrdir=a_bzrdir,
2017
 
                              _repository=found_repository,
 
2070
                              _repository=a_bzrdir.find_repository(),
2018
2071
                              ignore_fallbacks=ignore_fallbacks)
2019
2072
        except errors.NoSuchFile:
2020
2073
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2027
2080
    def supports_tags(self):
2028
2081
        return True
2029
2082
 
2030
 
    def supports_leaving_lock(self):
2031
 
        return True
2032
 
 
2033
2083
 
2034
2084
class BzrBranchFormat5(BranchFormatMetadir):
2035
2085
    """Bzr branch format 5.
2055
2105
        """See BranchFormat.get_format_description()."""
2056
2106
        return "Branch format 5"
2057
2107
 
2058
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2108
    def initialize(self, a_bzrdir, name=None):
2059
2109
        """Create a branch of this format in a_bzrdir."""
2060
2110
        utf8_files = [('revision-history', ''),
2061
2111
                      ('branch-name', ''),
2062
2112
                      ]
2063
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2113
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2064
2114
 
2065
2115
    def supports_tags(self):
2066
2116
        return False
2088
2138
        """See BranchFormat.get_format_description()."""
2089
2139
        return "Branch format 6"
2090
2140
 
2091
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2141
    def initialize(self, a_bzrdir, name=None):
2092
2142
        """Create a branch of this format in a_bzrdir."""
2093
2143
        utf8_files = [('last-revision', '0 null:\n'),
2094
2144
                      ('branch.conf', ''),
2095
2145
                      ('tags', ''),
2096
2146
                      ]
2097
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2147
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2098
2148
 
2099
2149
    def make_tags(self, branch):
2100
2150
        """See bzrlib.branch.BranchFormat.make_tags()."""
2118
2168
        """See BranchFormat.get_format_description()."""
2119
2169
        return "Branch format 8"
2120
2170
 
2121
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2171
    def initialize(self, a_bzrdir, name=None):
2122
2172
        """Create a branch of this format in a_bzrdir."""
2123
2173
        utf8_files = [('last-revision', '0 null:\n'),
2124
2174
                      ('branch.conf', ''),
2125
2175
                      ('tags', ''),
2126
2176
                      ('references', '')
2127
2177
                      ]
2128
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2178
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2179
 
 
2180
    def __init__(self):
 
2181
        super(BzrBranchFormat8, self).__init__()
 
2182
        self._matchingbzrdir.repository_format = \
 
2183
            RepositoryFormatKnitPack5RichRoot()
2129
2184
 
2130
2185
    def make_tags(self, branch):
2131
2186
        """See bzrlib.branch.BranchFormat.make_tags()."""
2140
2195
    supports_reference_locations = True
2141
2196
 
2142
2197
 
2143
 
class BzrBranchFormat7(BranchFormatMetadir):
 
2198
class BzrBranchFormat7(BzrBranchFormat8):
2144
2199
    """Branch format with last-revision, tags, and a stacked location pointer.
2145
2200
 
2146
2201
    The stacked location pointer is passed down to the repository and requires
2149
2204
    This format was introduced in bzr 1.6.
2150
2205
    """
2151
2206
 
2152
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2207
    def initialize(self, a_bzrdir, name=None):
2153
2208
        """Create a branch of this format in a_bzrdir."""
2154
2209
        utf8_files = [('last-revision', '0 null:\n'),
2155
2210
                      ('branch.conf', ''),
2156
2211
                      ('tags', ''),
2157
2212
                      ]
2158
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2213
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2159
2214
 
2160
2215
    def _branch_class(self):
2161
2216
        return BzrBranch7
2171
2226
    def supports_set_append_revisions_only(self):
2172
2227
        return True
2173
2228
 
2174
 
    def supports_stacking(self):
2175
 
        return True
2176
 
 
2177
 
    def make_tags(self, branch):
2178
 
        """See bzrlib.branch.BranchFormat.make_tags()."""
2179
 
        return BasicTags(branch)
2180
 
 
2181
2229
    supports_reference_locations = False
2182
2230
 
2183
2231
 
2210
2258
        transport = a_bzrdir.get_branch_transport(None, name=name)
2211
2259
        location = transport.put_bytes('location', to_branch.base)
2212
2260
 
2213
 
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2214
 
            repository=None):
 
2261
    def initialize(self, a_bzrdir, name=None, target_branch=None):
2215
2262
        """Create a branch of this format in a_bzrdir."""
2216
2263
        if target_branch is None:
2217
2264
            # this format does not implement branch itself, thus the implicit
2245
2292
        return clone
2246
2293
 
2247
2294
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2248
 
             possible_transports=None, ignore_fallbacks=False,
2249
 
             found_repository=None):
 
2295
             possible_transports=None, ignore_fallbacks=False):
2250
2296
        """Return the branch that the branch reference in a_bzrdir points at.
2251
2297
 
2252
2298
        :param a_bzrdir: A BzrDir that contains a branch.
2283
2329
        return result
2284
2330
 
2285
2331
 
2286
 
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
2287
 
    """Branch format registry."""
2288
 
 
2289
 
    def __init__(self, other_registry=None):
2290
 
        super(BranchFormatRegistry, self).__init__(other_registry)
2291
 
        self._default_format = None
2292
 
 
2293
 
    def set_default(self, format):
2294
 
        self._default_format = format
2295
 
 
2296
 
    def get_default(self):
2297
 
        return self._default_format
2298
 
 
2299
 
 
2300
2332
network_format_registry = registry.FormatRegistry()
2301
2333
"""Registry of formats indexed by their network name.
2302
2334
 
2305
2337
BranchFormat.network_name() for more detail.
2306
2338
"""
2307
2339
 
2308
 
format_registry = BranchFormatRegistry(network_format_registry)
2309
 
 
2310
2340
 
2311
2341
# formats which have no format string are not discoverable
2312
2342
# and not independently creatable, so are not registered.
2314
2344
__format6 = BzrBranchFormat6()
2315
2345
__format7 = BzrBranchFormat7()
2316
2346
__format8 = BzrBranchFormat8()
2317
 
format_registry.register(__format5)
2318
 
format_registry.register(BranchReferenceFormat())
2319
 
format_registry.register(__format6)
2320
 
format_registry.register(__format7)
2321
 
format_registry.register(__format8)
2322
 
format_registry.set_default(__format7)
 
2347
BranchFormat.register_format(__format5)
 
2348
BranchFormat.register_format(BranchReferenceFormat())
 
2349
BranchFormat.register_format(__format6)
 
2350
BranchFormat.register_format(__format7)
 
2351
BranchFormat.register_format(__format8)
 
2352
BranchFormat.set_default_format(__format7)
 
2353
_legacy_formats = [BzrBranchFormat4(),
 
2354
    ]
 
2355
network_format_registry.register(
 
2356
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2323
2357
 
2324
2358
 
2325
2359
class BranchWriteLockResult(LogicalLockResult):
2482
2516
            'revision-history', '\n'.join(history),
2483
2517
            mode=self.bzrdir._get_file_mode())
2484
2518
 
2485
 
    @deprecated_method(deprecated_in((2, 4, 0)))
 
2519
    @needs_write_lock
2486
2520
    def set_revision_history(self, rev_history):
2487
2521
        """See Branch.set_revision_history."""
2488
 
        self._set_revision_history(rev_history)
2489
 
 
2490
 
    @needs_write_lock
2491
 
    def _set_revision_history(self, rev_history):
2492
2522
        if 'evil' in debug.debug_flags:
2493
2523
            mutter_callsite(3, "set_revision_history scales with history.")
2494
2524
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2538
2568
            except ValueError:
2539
2569
                rev = self.repository.get_revision(revision_id)
2540
2570
                new_history = rev.get_history(self.repository)[1:]
2541
 
        destination._set_revision_history(new_history)
 
2571
        destination.set_revision_history(new_history)
2542
2572
 
2543
2573
    @needs_write_lock
2544
2574
    def set_last_revision_info(self, revno, revision_id):
2552
2582
        configured to check constraints on history, in which case this may not
2553
2583
        be permitted.
2554
2584
        """
2555
 
        if not revision_id or not isinstance(revision_id, basestring):
2556
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2585
        revision_id = _mod_revision.ensure_null(revision_id)
2557
2586
        # this old format stores the full history, but this api doesn't
2558
2587
        # provide it, so we must generate, and might as well check it's
2559
2588
        # correct
2560
2589
        history = self._lefthand_history(revision_id)
2561
2590
        if len(history) != revno:
2562
2591
            raise AssertionError('%d != %d' % (len(history), revno))
2563
 
        self._set_revision_history(history)
 
2592
        self.set_revision_history(history)
2564
2593
 
2565
2594
    def _gen_revision_history(self):
2566
2595
        history = self._transport.get_bytes('revision-history').split('\n')
2580
2609
        :param other_branch: The other branch that DivergedBranches should
2581
2610
            raise with respect to.
2582
2611
        """
2583
 
        self._set_revision_history(self._lefthand_history(revision_id,
 
2612
        self.set_revision_history(self._lefthand_history(revision_id,
2584
2613
            last_rev, other_branch))
2585
2614
 
2586
2615
    def basis_tree(self):
2596
2625
                pass
2597
2626
        return None
2598
2627
 
 
2628
    def _basic_push(self, target, overwrite, stop_revision):
 
2629
        """Basic implementation of push without bound branches or hooks.
 
2630
 
 
2631
        Must be called with source read locked and target write locked.
 
2632
        """
 
2633
        result = BranchPushResult()
 
2634
        result.source_branch = self
 
2635
        result.target_branch = target
 
2636
        result.old_revno, result.old_revid = target.last_revision_info()
 
2637
        self.update_references(target)
 
2638
        if result.old_revid != self.last_revision():
 
2639
            # We assume that during 'push' this repository is closer than
 
2640
            # the target.
 
2641
            graph = self.repository.get_graph(target.repository)
 
2642
            target.update_revisions(self, stop_revision,
 
2643
                overwrite=overwrite, graph=graph)
 
2644
        if self._push_should_merge_tags():
 
2645
            result.tag_conflicts = self.tags.merge_to(target.tags,
 
2646
                overwrite)
 
2647
        result.new_revno, result.new_revid = target.last_revision_info()
 
2648
        return result
 
2649
 
2599
2650
    def get_stacked_on_url(self):
2600
2651
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2601
2652
 
2630
2681
        """Return the branch we are bound to.
2631
2682
 
2632
2683
        :return: Either a Branch, or None
 
2684
 
 
2685
        This could memoise the branch, but if thats done
 
2686
        it must be revalidated on each new lock.
 
2687
        So for now we just don't memoise it.
 
2688
        # RBC 20060304 review this decision.
2633
2689
        """
2634
 
        if self._master_branch_cache is None:
2635
 
            self._master_branch_cache = self._get_master_branch(
2636
 
                possible_transports)
2637
 
        return self._master_branch_cache
2638
 
 
2639
 
    def _get_master_branch(self, possible_transports):
2640
2690
        bound_loc = self.get_bound_location()
2641
2691
        if not bound_loc:
2642
2692
            return None
2653
2703
 
2654
2704
        :param location: URL to the target branch
2655
2705
        """
2656
 
        self._master_branch_cache = None
2657
2706
        if location:
2658
2707
            self._transport.put_bytes('bound', location+'\n',
2659
2708
                mode=self.bzrdir._get_file_mode())
2768
2817
 
2769
2818
    @needs_write_lock
2770
2819
    def set_last_revision_info(self, revno, revision_id):
2771
 
        if not revision_id or not isinstance(revision_id, basestring):
2772
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2820
        revision_id = _mod_revision.ensure_null(revision_id)
2773
2821
        old_revno, old_revid = self.last_revision_info()
2774
2822
        if self._get_append_revisions_only():
2775
2823
            self._check_history_violation(revision_id)
2912
2960
 
2913
2961
    def set_bound_location(self, location):
2914
2962
        """See Branch.set_push_location."""
2915
 
        self._master_branch_cache = None
2916
2963
        result = None
2917
2964
        config = self.get_config()
2918
2965
        if location is None:
2995
3042
        try:
2996
3043
            index = self._partial_revision_history_cache.index(revision_id)
2997
3044
        except ValueError:
2998
 
            try:
2999
 
                self._extend_partial_history(stop_revision=revision_id)
3000
 
            except errors.RevisionNotPresent, e:
3001
 
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
 
3045
            self._extend_partial_history(stop_revision=revision_id)
3002
3046
            index = len(self._partial_revision_history_cache) - 1
3003
3047
            if self._partial_revision_history_cache[index] != revision_id:
3004
3048
                raise errors.NoSuchRevision(self, revision_id)
3251
3295
        raise NotImplementedError(self.pull)
3252
3296
 
3253
3297
    @needs_write_lock
 
3298
    def update_revisions(self, stop_revision=None, overwrite=False,
 
3299
                         graph=None):
 
3300
        """Pull in new perfect-fit revisions.
 
3301
 
 
3302
        :param stop_revision: Updated until the given revision
 
3303
        :param overwrite: Always set the branch pointer, rather than checking
 
3304
            to see if it is a proper descendant.
 
3305
        :param graph: A Graph object that can be used to query history
 
3306
            information. This can be None.
 
3307
        :return: None
 
3308
        """
 
3309
        raise NotImplementedError(self.update_revisions)
 
3310
 
 
3311
    @needs_write_lock
3254
3312
    def push(self, overwrite=False, stop_revision=None,
3255
3313
             _override_hook_source_branch=None):
3256
3314
        """Mirror the source branch into the target branch.
3268
3326
        """
3269
3327
        raise NotImplementedError(self.copy_content_into)
3270
3328
 
3271
 
    @needs_write_lock
3272
 
    def fetch(self, stop_revision=None):
3273
 
        """Fetch revisions.
3274
 
 
3275
 
        :param stop_revision: Last revision to fetch
3276
 
        """
3277
 
        raise NotImplementedError(self.fetch)
3278
 
 
3279
3329
 
3280
3330
class GenericInterBranch(InterBranch):
3281
3331
    """InterBranch implementation that uses public Branch functions."""
3287
3337
 
3288
3338
    @classmethod
3289
3339
    def _get_branch_formats_to_test(klass):
3290
 
        return [(format_registry.get_default(), format_registry.get_default())]
 
3340
        return [(BranchFormat._default_format, BranchFormat._default_format)]
3291
3341
 
3292
3342
    @classmethod
3293
3343
    def unwrap_format(klass, format):
3294
3344
        if isinstance(format, remote.RemoteBranchFormat):
3295
3345
            format._ensure_real()
3296
3346
            return format._custom_format
3297
 
        return format
 
3347
        return format                                                                                                  
3298
3348
 
3299
3349
    @needs_write_lock
3300
3350
    def copy_content_into(self, revision_id=None):
3316
3366
            self.source.tags.merge_to(self.target.tags)
3317
3367
 
3318
3368
    @needs_write_lock
3319
 
    def fetch(self, stop_revision=None):
3320
 
        if self.target.base == self.source.base:
3321
 
            return (0, [])
3322
 
        self.source.lock_read()
3323
 
        try:
3324
 
            fetch_spec_factory = fetch.FetchSpecFactory()
3325
 
            fetch_spec_factory.source_branch = self.source
3326
 
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3327
 
            fetch_spec_factory.source_repo = self.source.repository
3328
 
            fetch_spec_factory.target_repo = self.target.repository
3329
 
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3330
 
            fetch_spec = fetch_spec_factory.make_fetch_spec()
3331
 
            return self.target.repository.fetch(self.source.repository,
3332
 
                fetch_spec=fetch_spec)
3333
 
        finally:
3334
 
            self.source.unlock()
3335
 
 
3336
 
    @needs_write_lock
3337
 
    def _update_revisions(self, stop_revision=None, overwrite=False,
3338
 
            graph=None):
 
3369
    def update_revisions(self, stop_revision=None, overwrite=False,
 
3370
        graph=None):
 
3371
        """See InterBranch.update_revisions()."""
3339
3372
        other_revno, other_last_revision = self.source.last_revision_info()
3340
3373
        stop_revno = None # unknown
3341
3374
        if stop_revision is None:
3352
3385
        # case of having something to pull, and so that the check for
3353
3386
        # already merged can operate on the just fetched graph, which will
3354
3387
        # be cached in memory.
3355
 
        self.fetch(stop_revision=stop_revision)
 
3388
        self.target.fetch(self.source, stop_revision)
3356
3389
        # Check to see if one is an ancestor of the other
3357
3390
        if not overwrite:
3358
3391
            if graph is None:
3386
3419
        if local and not bound_location:
3387
3420
            raise errors.LocalRequiresBoundBranch()
3388
3421
        master_branch = None
3389
 
        source_is_master = (self.source.user_url == bound_location)
3390
 
        if not local and bound_location and not source_is_master:
 
3422
        if not local and bound_location and self.source.user_url != bound_location:
3391
3423
            # not pulling from master, so we need to update master.
3392
3424
            master_branch = self.target.get_master_branch(possible_transports)
3393
3425
            master_branch.lock_write()
3399
3431
            return self._pull(overwrite,
3400
3432
                stop_revision, _hook_master=master_branch,
3401
3433
                run_hooks=run_hooks,
3402
 
                _override_hook_target=_override_hook_target,
3403
 
                merge_tags_to_master=not source_is_master)
 
3434
                _override_hook_target=_override_hook_target)
3404
3435
        finally:
3405
3436
            if master_branch:
3406
3437
                master_branch.unlock()
3427
3458
        finally:
3428
3459
            self.source.unlock()
3429
3460
 
3430
 
    def _basic_push(self, overwrite, stop_revision):
3431
 
        """Basic implementation of push without bound branches or hooks.
3432
 
 
3433
 
        Must be called with source read locked and target write locked.
3434
 
        """
3435
 
        result = BranchPushResult()
3436
 
        result.source_branch = self.source
3437
 
        result.target_branch = self.target
3438
 
        result.old_revno, result.old_revid = self.target.last_revision_info()
3439
 
        self.source.update_references(self.target)
3440
 
        if result.old_revid != stop_revision:
3441
 
            # We assume that during 'push' this repository is closer than
3442
 
            # the target.
3443
 
            graph = self.source.repository.get_graph(self.target.repository)
3444
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3445
 
                    graph=graph)
3446
 
        if self.source._push_should_merge_tags():
3447
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3448
 
                overwrite)
3449
 
        result.new_revno, result.new_revid = self.target.last_revision_info()
3450
 
        return result
3451
 
 
3452
3461
    def _push_with_bound_branches(self, overwrite, stop_revision,
3453
3462
            _override_hook_source_branch=None):
3454
3463
        """Push from source into target, and into target's master if any.
3469
3478
            master_branch.lock_write()
3470
3479
            try:
3471
3480
                # push into the master from the source branch.
3472
 
                master_inter = InterBranch.get(self.source, master_branch)
3473
 
                master_inter._basic_push(overwrite, stop_revision)
3474
 
                # and push into the target branch from the source. Note that
3475
 
                # we push from the source branch again, because it's considered
3476
 
                # the highest bandwidth repository.
3477
 
                result = self._basic_push(overwrite, stop_revision)
 
3481
                self.source._basic_push(master_branch, overwrite, stop_revision)
 
3482
                # and push into the target branch from the source. Note that we
 
3483
                # push from the source branch again, because it's considered the
 
3484
                # highest bandwidth repository.
 
3485
                result = self.source._basic_push(self.target, overwrite,
 
3486
                    stop_revision)
3478
3487
                result.master_branch = master_branch
3479
3488
                result.local_branch = self.target
3480
3489
                _run_hooks()
3483
3492
                master_branch.unlock()
3484
3493
        else:
3485
3494
            # no master branch
3486
 
            result = self._basic_push(overwrite, stop_revision)
 
3495
            result = self.source._basic_push(self.target, overwrite,
 
3496
                stop_revision)
3487
3497
            # TODO: Why set master_branch and local_branch if there's no
3488
3498
            # binding?  Maybe cleaner to just leave them unset? -- mbp
3489
3499
            # 20070504
3494
3504
 
3495
3505
    def _pull(self, overwrite=False, stop_revision=None,
3496
3506
             possible_transports=None, _hook_master=None, run_hooks=True,
3497
 
             _override_hook_target=None, local=False,
3498
 
             merge_tags_to_master=True):
 
3507
             _override_hook_target=None, local=False):
3499
3508
        """See Branch.pull.
3500
3509
 
3501
3510
        This function is the core worker, used by GenericInterBranch.pull to
3506
3515
        :param run_hooks: Private parameter - if false, this branch
3507
3516
            is being called because it's the master of the primary branch,
3508
3517
            so it should not run its hooks.
3509
 
            is being called because it's the master of the primary branch,
3510
 
            so it should not run its hooks.
3511
3518
        :param _override_hook_target: Private parameter - set the branch to be
3512
3519
            supplied as the target_branch to pull hooks.
3513
3520
        :param local: Only update the local branch, and not the bound branch.
3532
3539
            # -- JRV20090506
3533
3540
            result.old_revno, result.old_revid = \
3534
3541
                self.target.last_revision_info()
3535
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3536
 
                graph=graph)
 
3542
            self.target.update_revisions(self.source, stop_revision,
 
3543
                overwrite=overwrite, graph=graph)
3537
3544
            # TODO: The old revid should be specified when merging tags, 
3538
3545
            # so a tags implementation that versions tags can only 
3539
3546
            # pull in the most recent changes. -- JRV20090506
3540
3547
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3541
 
                overwrite, ignore_master=not merge_tags_to_master)
 
3548
                overwrite)
3542
3549
            result.new_revno, result.new_revid = self.target.last_revision_info()
3543
3550
            if _hook_master:
3544
3551
                result.master_branch = _hook_master