~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Jonathan Riddell
  • Date: 2011-05-16 10:05:25 UTC
  • mto: This revision was merged to the branch mainline in revision 5869.
  • Revision ID: jriddell@canonical.com-20110516100525-7q23m5opdnl4qg41
start adding licences

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 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
27
27
        config as _mod_config,
28
28
        debug,
29
29
        errors,
 
30
        fetch,
 
31
        graph as _mod_graph,
30
32
        lockdir,
31
33
        lockable_files,
32
34
        remote,
40
42
        urlutils,
41
43
        )
42
44
from bzrlib.config import BranchConfig, TransportConfig
43
 
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
44
45
from bzrlib.tag import (
45
46
    BasicTags,
46
47
    DisabledTags,
47
48
    )
48
49
""")
49
50
 
50
 
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
51
 
from bzrlib.hooks import HookPoint, Hooks
 
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
52
60
from bzrlib.inter import InterObject
53
61
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
54
62
from bzrlib import registry
64
72
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
65
73
 
66
74
 
67
 
class Branch(bzrdir.ControlComponent):
 
75
class Branch(controldir.ControlComponent):
68
76
    """Branch holding a history of revisions.
69
77
 
70
78
    :ivar base:
71
79
        Base directory/url of the branch; using control_url and
72
80
        control_transport is more standardized.
73
 
 
74
 
    hooks: An instance of BranchHooks.
 
81
    :ivar hooks: An instance of BranchHooks.
 
82
    :ivar _master_branch_cache: cached result of get_master_branch, see
 
83
        _clear_cached_state.
75
84
    """
76
85
    # this is really an instance variable - FIXME move it there
77
86
    # - RBC 20060112
91
100
        self._revision_id_to_revno_cache = None
92
101
        self._partial_revision_id_to_revno_cache = {}
93
102
        self._partial_revision_history_cache = []
 
103
        self._tags_bytes = None
94
104
        self._last_revision_info_cache = None
 
105
        self._master_branch_cache = None
95
106
        self._merge_sorted_revisions_cache = None
96
107
        self._open_hook()
97
108
        hooks = Branch.hooks['open']
103
114
 
104
115
    def _activate_fallback_location(self, url):
105
116
        """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
106
124
        repo = self._get_fallback_repository(url)
107
125
        if repo.has_same_location(self.repository):
108
126
            raise errors.UnstackableLocationError(self.user_url, url)
226
244
            possible_transports=[self.bzrdir.root_transport])
227
245
        return a_branch.repository
228
246
 
 
247
    @needs_read_lock
229
248
    def _get_tags_bytes(self):
230
249
        """Get the bytes of a serialised tags dict.
231
250
 
238
257
        :return: The bytes of the tags file.
239
258
        :seealso: Branch._set_tags_bytes.
240
259
        """
241
 
        return self._transport.get_bytes('tags')
 
260
        if self._tags_bytes is None:
 
261
            self._tags_bytes = self._transport.get_bytes('tags')
 
262
        return self._tags_bytes
242
263
 
243
264
    def _get_nick(self, local=False, possible_transports=None):
244
265
        config = self.get_config()
648
669
        raise errors.UnsupportedOperation(self.get_reference_info, self)
649
670
 
650
671
    @needs_write_lock
651
 
    def fetch(self, from_branch, last_revision=None, pb=None):
 
672
    def fetch(self, from_branch, last_revision=None):
652
673
        """Copy revisions from from_branch into this branch.
653
674
 
654
675
        :param from_branch: Where to copy from.
655
676
        :param last_revision: What revision to stop at (None for at the end
656
677
                              of the branch.
657
 
        :param pb: An optional progress bar to use.
658
678
        :return: None
659
679
        """
660
 
        if self.base == from_branch.base:
661
 
            return (0, [])
662
 
        if pb is not None:
663
 
            symbol_versioning.warn(
664
 
                symbol_versioning.deprecated_in((1, 14, 0))
665
 
                % "pb parameter to fetch()")
666
 
        from_branch.lock_read()
667
 
        try:
668
 
            if last_revision is None:
669
 
                last_revision = from_branch.last_revision()
670
 
                last_revision = _mod_revision.ensure_null(last_revision)
671
 
            return self.repository.fetch(from_branch.repository,
672
 
                                         revision_id=last_revision,
673
 
                                         pb=pb)
674
 
        finally:
675
 
            from_branch.unlock()
 
680
        return InterBranch.get(from_branch, self).fetch(last_revision)
676
681
 
677
682
    def get_bound_location(self):
678
683
        """Return the URL of the branch we are bound to.
689
694
 
690
695
    def get_commit_builder(self, parents, config=None, timestamp=None,
691
696
                           timezone=None, committer=None, revprops=None,
692
 
                           revision_id=None):
 
697
                           revision_id=None, lossy=False):
693
698
        """Obtain a CommitBuilder for this branch.
694
699
 
695
700
        :param parents: Revision ids of the parents of the new revision.
699
704
        :param committer: Optional committer to set for commit.
700
705
        :param revprops: Optional dictionary of revision properties.
701
706
        :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 
702
709
        """
703
710
 
704
711
        if config is None:
705
712
            config = self.get_config()
706
713
 
707
714
        return self.repository.get_commit_builder(self, parents, config,
708
 
            timestamp, timezone, committer, revprops, revision_id)
 
715
            timestamp, timezone, committer, revprops, revision_id,
 
716
            lossy)
709
717
 
710
718
    def get_master_branch(self, possible_transports=None):
711
719
        """Return the branch we are bound to.
738
746
        """Print `file` to stdout."""
739
747
        raise NotImplementedError(self.print_file)
740
748
 
 
749
    @deprecated_method(deprecated_in((2, 4, 0)))
741
750
    def set_revision_history(self, rev_history):
742
 
        raise NotImplementedError(self.set_revision_history)
 
751
        """See Branch.set_revision_history."""
 
752
        self._set_revision_history(rev_history)
 
753
 
 
754
    @needs_write_lock
 
755
    def _set_revision_history(self, rev_history):
 
756
        if len(rev_history) == 0:
 
757
            revid = _mod_revision.NULL_REVISION
 
758
        else:
 
759
            revid = rev_history[-1]
 
760
        if rev_history != self._lefthand_history(revid):
 
761
            raise errors.NotLefthandHistory(rev_history)
 
762
        self.set_last_revision_info(len(rev_history), revid)
 
763
        self._cache_revision_history(rev_history)
 
764
        for hook in Branch.hooks['set_rh']:
 
765
            hook(self, rev_history)
 
766
 
 
767
    @needs_write_lock
 
768
    def set_last_revision_info(self, revno, revision_id):
 
769
        """Set the last revision of this branch.
 
770
 
 
771
        The caller is responsible for checking that the revno is correct
 
772
        for this revision id.
 
773
 
 
774
        It may be possible to set the branch last revision to an id not
 
775
        present in the repository.  However, branches can also be
 
776
        configured to check constraints on history, in which case this may not
 
777
        be permitted.
 
778
        """
 
779
        raise NotImplementedError(self.last_revision_info)
 
780
 
 
781
    @needs_write_lock
 
782
    def generate_revision_history(self, revision_id, last_rev=None,
 
783
                                  other_branch=None):
 
784
        """See Branch.generate_revision_history"""
 
785
        # FIXME: This shouldn't have to fetch the entire history
 
786
        history = self._lefthand_history(revision_id, last_rev, other_branch)
 
787
        revno = len(history)
 
788
        self.set_last_revision_info(revno, revision_id)
 
789
        self._cache_revision_history(history)
743
790
 
744
791
    @needs_write_lock
745
792
    def set_parent(self, url):
789
836
 
790
837
    def _unstack(self):
791
838
        """Change a branch to be unstacked, copying data as needed.
792
 
        
 
839
 
793
840
        Don't call this directly, use set_stacked_on_url(None).
794
841
        """
795
842
        pb = ui.ui_factory.nested_progress_bar()
804
851
            old_repository = self.repository
805
852
            if len(old_repository._fallback_repositories) != 1:
806
853
                raise AssertionError("can't cope with fallback repositories "
807
 
                    "of %r" % (self.repository,))
 
854
                    "of %r (fallbacks: %r)" % (old_repository,
 
855
                        old_repository._fallback_repositories))
808
856
            # Open the new repository object.
809
857
            # Repositories don't offer an interface to remove fallback
810
858
            # repositories today; take the conceptually simpler option and just
858
906
                # XXX: If you unstack a branch while it has a working tree
859
907
                # with a pending merge, the pending-merged revisions will no
860
908
                # longer be present.  You can (probably) revert and remerge.
861
 
                #
862
 
                # XXX: This only fetches up to the tip of the repository; it
863
 
                # doesn't bring across any tags.  That's fairly consistent
864
 
                # with how branch works, but perhaps not ideal.
865
 
                self.repository.fetch(old_repository,
866
 
                    revision_id=self.last_revision(),
867
 
                    find_ghosts=True)
 
909
                try:
 
910
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
 
911
                except errors.TagsNotSupported:
 
912
                    tags_to_fetch = set()
 
913
                fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
 
914
                    old_repository, required_ids=[self.last_revision()],
 
915
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
 
916
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
868
917
            finally:
869
918
                old_repository.unlock()
870
919
        finally:
875
924
 
876
925
        :seealso: Branch._get_tags_bytes.
877
926
        """
878
 
        return _run_with_write_locked_target(self, self._transport.put_bytes,
879
 
            'tags', bytes)
 
927
        return _run_with_write_locked_target(self, self._set_tags_bytes_locked,
 
928
                bytes)
 
929
 
 
930
    def _set_tags_bytes_locked(self, bytes):
 
931
        self._tags_bytes = bytes
 
932
        return self._transport.put_bytes('tags', bytes)
880
933
 
881
934
    def _cache_revision_history(self, rev_history):
882
935
        """Set the cached revision history to rev_history.
909
962
        self._revision_history_cache = None
910
963
        self._revision_id_to_revno_cache = None
911
964
        self._last_revision_info_cache = None
 
965
        self._master_branch_cache = None
912
966
        self._merge_sorted_revisions_cache = None
913
967
        self._partial_revision_history_cache = []
914
968
        self._partial_revision_id_to_revno_cache = {}
 
969
        self._tags_bytes = None
915
970
 
916
971
    def _gen_revision_history(self):
917
972
        """Return sequence of revision hashes on to this branch.
967
1022
        :return: A tuple (revno, revision_id).
968
1023
        """
969
1024
        if self._last_revision_info_cache is None:
970
 
            self._last_revision_info_cache = self._last_revision_info()
 
1025
            self._last_revision_info_cache = self._read_last_revision_info()
971
1026
        return self._last_revision_info_cache
972
1027
 
973
 
    def _last_revision_info(self):
974
 
        rh = self.revision_history()
975
 
        revno = len(rh)
976
 
        if revno:
977
 
            return (revno, rh[-1])
978
 
        else:
979
 
            return (0, _mod_revision.NULL_REVISION)
980
 
 
981
 
    @deprecated_method(deprecated_in((1, 6, 0)))
982
 
    def missing_revisions(self, other, stop_revision=None):
983
 
        """Return a list of new revisions that would perfectly fit.
984
 
 
985
 
        If self and other have not diverged, return a list of the revisions
986
 
        present in other, but missing from self.
987
 
        """
988
 
        self_history = self.revision_history()
989
 
        self_len = len(self_history)
990
 
        other_history = other.revision_history()
991
 
        other_len = len(other_history)
992
 
        common_index = min(self_len, other_len) -1
993
 
        if common_index >= 0 and \
994
 
            self_history[common_index] != other_history[common_index]:
995
 
            raise errors.DivergedBranches(self, other)
996
 
 
997
 
        if stop_revision is None:
998
 
            stop_revision = other_len
999
 
        else:
1000
 
            if stop_revision > other_len:
1001
 
                raise errors.NoSuchRevision(self, stop_revision)
1002
 
        return other_history[self_len:stop_revision]
1003
 
 
1004
 
    def update_revisions(self, other, stop_revision=None, overwrite=False,
1005
 
                         graph=None):
1006
 
        """Pull in new perfect-fit revisions.
1007
 
 
1008
 
        :param other: Another Branch to pull from
1009
 
        :param stop_revision: Updated until the given revision
1010
 
        :param overwrite: Always set the branch pointer, rather than checking
1011
 
            to see if it is a proper descendant.
1012
 
        :param graph: A Graph object that can be used to query history
1013
 
            information. This can be None.
1014
 
        :return: None
1015
 
        """
1016
 
        return InterBranch.get(other, self).update_revisions(stop_revision,
1017
 
            overwrite, graph)
1018
 
 
 
1028
    def _read_last_revision_info(self):
 
1029
        raise NotImplementedError(self._read_last_revision_info)
 
1030
 
 
1031
    @deprecated_method(deprecated_in((2, 4, 0)))
1019
1032
    def import_last_revision_info(self, source_repo, revno, revid):
1020
1033
        """Set the last revision info, importing from another repo if necessary.
1021
1034
 
1022
 
        This is used by the bound branch code to upload a revision to
1023
 
        the master branch first before updating the tip of the local branch.
1024
 
 
1025
1035
        :param source_repo: Source repository to optionally fetch from
1026
1036
        :param revno: Revision number of the new tip
1027
1037
        :param revid: Revision id of the new tip
1030
1040
            self.repository.fetch(source_repo, revision_id=revid)
1031
1041
        self.set_last_revision_info(revno, revid)
1032
1042
 
 
1043
    def import_last_revision_info_and_tags(self, source, revno, revid,
 
1044
                                           lossy=False):
 
1045
        """Set the last revision info, importing from another repo if necessary.
 
1046
 
 
1047
        This is used by the bound branch code to upload a revision to
 
1048
        the master branch first before updating the tip of the local branch.
 
1049
        Revisions referenced by source's tags are also transferred.
 
1050
 
 
1051
        :param source: Source branch to optionally fetch from
 
1052
        :param revno: Revision number of the new tip
 
1053
        :param revid: Revision id of the new tip
 
1054
        :param lossy: Whether to discard metadata that can not be
 
1055
            natively represented
 
1056
        :return: Tuple with the new revision number and revision id
 
1057
            (should only be different from the arguments when lossy=True)
 
1058
        """
 
1059
        if not self.repository.has_same_location(source.repository):
 
1060
            self.fetch(source, revid)
 
1061
        self.set_last_revision_info(revno, revid)
 
1062
        return (revno, revid)
 
1063
 
1033
1064
    def revision_id_to_revno(self, revision_id):
1034
1065
        """Given a revision id, return its revno"""
1035
1066
        if _mod_revision.is_null(revision_id):
1256
1287
        return result
1257
1288
 
1258
1289
    @needs_read_lock
1259
 
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
 
1290
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
 
1291
            repository=None):
1260
1292
        """Create a new line of development from the branch, into to_bzrdir.
1261
1293
 
1262
1294
        to_bzrdir controls the branch format.
1267
1299
        if (repository_policy is not None and
1268
1300
            repository_policy.requires_stacking()):
1269
1301
            to_bzrdir._format.require_stacking(_skip_repo=True)
1270
 
        result = to_bzrdir.create_branch()
 
1302
        result = to_bzrdir.create_branch(repository=repository)
1271
1303
        result.lock_write()
1272
1304
        try:
1273
1305
            if repository_policy is not None:
1274
1306
                repository_policy.configure_branch(result)
1275
1307
            self.copy_content_into(result, revision_id=revision_id)
1276
 
            result.set_parent(self.bzrdir.root_transport.base)
 
1308
            master_branch = self.get_master_branch()
 
1309
            if master_branch is None:
 
1310
                result.set_parent(self.bzrdir.root_transport.base)
 
1311
            else:
 
1312
                result.set_parent(master_branch.bzrdir.root_transport.base)
1277
1313
        finally:
1278
1314
            result.unlock()
1279
1315
        return result
1361
1397
        """Return the most suitable metadir for a checkout of this branch.
1362
1398
        Weaves are used if this branch's repository uses weaves.
1363
1399
        """
1364
 
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
1365
 
            from bzrlib.repofmt import weaverepo
1366
 
            format = bzrdir.BzrDirMetaFormat1()
1367
 
            format.repository_format = weaverepo.RepositoryFormat7()
1368
 
        else:
1369
 
            format = self.repository.bzrdir.checkout_metadir()
1370
 
            format.set_branch_format(self._format)
 
1400
        format = self.repository.bzrdir.checkout_metadir()
 
1401
        format.set_branch_format(self._format)
1371
1402
        return format
1372
1403
 
1373
1404
    def create_clone_on_transport(self, to_transport, revision_id=None,
1374
 
        stacked_on=None, create_prefix=False, use_existing_dir=False):
 
1405
        stacked_on=None, create_prefix=False, use_existing_dir=False,
 
1406
        no_tree=None):
1375
1407
        """Create a clone of this branch and its bzrdir.
1376
1408
 
1377
1409
        :param to_transport: The transport to clone onto.
1390
1422
            revision_id = self.last_revision()
1391
1423
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1392
1424
            revision_id=revision_id, stacked_on=stacked_on,
1393
 
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
 
1425
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
 
1426
            no_tree=no_tree)
1394
1427
        return dir_to.open_branch()
1395
1428
 
1396
1429
    def create_checkout(self, to_location, revision_id=None,
1511
1544
        else:
1512
1545
            raise AssertionError("invalid heads: %r" % (heads,))
1513
1546
 
1514
 
 
1515
 
class BranchFormat(object):
 
1547
    def heads_to_fetch(self):
 
1548
        """Return the heads that must and that should be fetched to copy this
 
1549
        branch into another repo.
 
1550
 
 
1551
        :returns: a 2-tuple of (must_fetch, if_present_fetch).  must_fetch is a
 
1552
            set of heads that must be fetched.  if_present_fetch is a set of
 
1553
            heads that must be fetched if present, but no error is necessary if
 
1554
            they are not present.
 
1555
        """
 
1556
        # For bzr native formats must_fetch is just the tip, and if_present_fetch
 
1557
        # are the tags.
 
1558
        must_fetch = set([self.last_revision()])
 
1559
        try:
 
1560
            if_present_fetch = set(self.tags.get_reverse_tag_dict())
 
1561
        except errors.TagsNotSupported:
 
1562
            if_present_fetch = set()
 
1563
        must_fetch.discard(_mod_revision.NULL_REVISION)
 
1564
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
 
1565
        return must_fetch, if_present_fetch
 
1566
 
 
1567
 
 
1568
class BranchFormat(controldir.ControlComponentFormat):
1516
1569
    """An encapsulation of the initialization and open routines for a format.
1517
1570
 
1518
1571
    Formats provide three things:
1521
1574
     * an open routine.
1522
1575
 
1523
1576
    Formats are placed in an dict by their format string for reference
1524
 
    during branch opening. Its not required that these be instances, they
 
1577
    during branch opening. It's not required that these be instances, they
1525
1578
    can be classes themselves with class methods - it simply depends on
1526
1579
    whether state is needed for a given format or not.
1527
1580
 
1530
1583
    object will be created every time regardless.
1531
1584
    """
1532
1585
 
1533
 
    _default_format = None
1534
 
    """The default format used for new branches."""
1535
 
 
1536
 
    _formats = {}
1537
 
    """The known formats."""
1538
 
 
1539
1586
    can_set_append_revisions_only = True
1540
1587
 
1541
1588
    def __eq__(self, other):
1550
1597
        try:
1551
1598
            transport = a_bzrdir.get_branch_transport(None, name=name)
1552
1599
            format_string = transport.get_bytes("format")
1553
 
            format = klass._formats[format_string]
1554
 
            if isinstance(format, MetaDirBranchFormatFactory):
1555
 
                return format()
1556
 
            return format
 
1600
            return format_registry.get(format_string)
1557
1601
        except errors.NoSuchFile:
1558
1602
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1559
1603
        except KeyError:
1560
1604
            raise errors.UnknownFormatError(format=format_string, kind='branch')
1561
1605
 
1562
1606
    @classmethod
 
1607
    @deprecated_method(deprecated_in((2, 4, 0)))
1563
1608
    def get_default_format(klass):
1564
1609
        """Return the current default format."""
1565
 
        return klass._default_format
 
1610
        return format_registry.get_default()
1566
1611
 
1567
1612
    @classmethod
 
1613
    @deprecated_method(deprecated_in((2, 4, 0)))
1568
1614
    def get_formats(klass):
1569
1615
        """Get all the known formats.
1570
1616
 
1571
1617
        Warning: This triggers a load of all lazy registered formats: do not
1572
1618
        use except when that is desireed.
1573
1619
        """
1574
 
        result = []
1575
 
        for fmt in klass._formats.values():
1576
 
            if isinstance(fmt, MetaDirBranchFormatFactory):
1577
 
                fmt = fmt()
1578
 
            result.append(fmt)
1579
 
        return result
 
1620
        return format_registry._get_all()
1580
1621
 
1581
1622
    def get_reference(self, a_bzrdir, name=None):
1582
1623
        """Get the target reference of the branch in a_bzrdir.
1621
1662
        for hook in hooks:
1622
1663
            hook(params)
1623
1664
 
1624
 
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1625
 
                           lock_type='metadir', set_format=True):
1626
 
        """Initialize a branch in a bzrdir, with specified files
1627
 
 
1628
 
        :param a_bzrdir: The bzrdir to initialize the branch in
1629
 
        :param utf8_files: The files to create as a list of
1630
 
            (filename, content) tuples
1631
 
        :param name: Name of colocated branch to create, if any
1632
 
        :param set_format: If True, set the format with
1633
 
            self.get_format_string.  (BzrBranch4 has its format set
1634
 
            elsewhere)
1635
 
        :return: a branch in this format
1636
 
        """
1637
 
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1638
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1639
 
        lock_map = {
1640
 
            'metadir': ('lock', lockdir.LockDir),
1641
 
            'branch4': ('branch-lock', lockable_files.TransportLock),
1642
 
        }
1643
 
        lock_name, lock_class = lock_map[lock_type]
1644
 
        control_files = lockable_files.LockableFiles(branch_transport,
1645
 
            lock_name, lock_class)
1646
 
        control_files.create_lock()
1647
 
        try:
1648
 
            control_files.lock_write()
1649
 
        except errors.LockContention:
1650
 
            if lock_type != 'branch4':
1651
 
                raise
1652
 
            lock_taken = False
1653
 
        else:
1654
 
            lock_taken = True
1655
 
        if set_format:
1656
 
            utf8_files += [('format', self.get_format_string())]
1657
 
        try:
1658
 
            for (filename, content) in utf8_files:
1659
 
                branch_transport.put_bytes(
1660
 
                    filename, content,
1661
 
                    mode=a_bzrdir._get_file_mode())
1662
 
        finally:
1663
 
            if lock_taken:
1664
 
                control_files.unlock()
1665
 
        branch = self.open(a_bzrdir, name, _found=True)
1666
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1667
 
        return branch
1668
 
 
1669
 
    def initialize(self, a_bzrdir, name=None):
 
1665
    def initialize(self, a_bzrdir, name=None, repository=None):
1670
1666
        """Create a branch of this format in a_bzrdir.
1671
1667
        
1672
1668
        :param name: Name of the colocated branch to create.
1706
1702
        """
1707
1703
        raise NotImplementedError(self.network_name)
1708
1704
 
1709
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
1705
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
 
1706
            found_repository=None):
1710
1707
        """Return the branch object for a_bzrdir
1711
1708
 
1712
1709
        :param a_bzrdir: A BzrDir that contains a branch.
1719
1716
        raise NotImplementedError(self.open)
1720
1717
 
1721
1718
    @classmethod
 
1719
    @deprecated_method(deprecated_in((2, 4, 0)))
1722
1720
    def register_format(klass, format):
1723
1721
        """Register a metadir format.
1724
 
        
 
1722
 
1725
1723
        See MetaDirBranchFormatFactory for the ability to register a format
1726
1724
        without loading the code the format needs until it is actually used.
1727
1725
        """
1728
 
        klass._formats[format.get_format_string()] = format
1729
 
        # Metadir formats have a network name of their format string, and get
1730
 
        # registered as factories.
1731
 
        if isinstance(format, MetaDirBranchFormatFactory):
1732
 
            network_format_registry.register(format.get_format_string(), format)
1733
 
        else:
1734
 
            network_format_registry.register(format.get_format_string(),
1735
 
                format.__class__)
 
1726
        format_registry.register(format)
1736
1727
 
1737
1728
    @classmethod
 
1729
    @deprecated_method(deprecated_in((2, 4, 0)))
1738
1730
    def set_default_format(klass, format):
1739
 
        klass._default_format = format
 
1731
        format_registry.set_default(format)
1740
1732
 
1741
1733
    def supports_set_append_revisions_only(self):
1742
1734
        """True if this format supports set_append_revisions_only."""
1746
1738
        """True if this format records a stacked-on branch."""
1747
1739
        return False
1748
1740
 
 
1741
    def supports_leaving_lock(self):
 
1742
        """True if this format supports leaving locks in place."""
 
1743
        return False # by default
 
1744
 
1749
1745
    @classmethod
 
1746
    @deprecated_method(deprecated_in((2, 4, 0)))
1750
1747
    def unregister_format(klass, format):
1751
 
        del klass._formats[format.get_format_string()]
 
1748
        format_registry.remove(format)
1752
1749
 
1753
1750
    def __str__(self):
1754
1751
        return self.get_format_description().rstrip()
1799
1796
        These are all empty initially, because by default nothing should get
1800
1797
        notified.
1801
1798
        """
1802
 
        Hooks.__init__(self)
1803
 
        self.create_hook(HookPoint('set_rh',
 
1799
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
 
1800
        self.add_hook('set_rh',
1804
1801
            "Invoked whenever the revision history has been set via "
1805
1802
            "set_revision_history. The api signature is (branch, "
1806
1803
            "revision_history), and the branch will be write-locked. "
1807
1804
            "The set_rh hook can be expensive for bzr to trigger, a better "
1808
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1809
 
        self.create_hook(HookPoint('open',
 
1805
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
 
1806
        self.add_hook('open',
1810
1807
            "Called with the Branch object that has been opened after a "
1811
 
            "branch is opened.", (1, 8), None))
1812
 
        self.create_hook(HookPoint('post_push',
 
1808
            "branch is opened.", (1, 8))
 
1809
        self.add_hook('post_push',
1813
1810
            "Called after a push operation completes. post_push is called "
1814
1811
            "with a bzrlib.branch.BranchPushResult object and only runs in the "
1815
 
            "bzr client.", (0, 15), None))
1816
 
        self.create_hook(HookPoint('post_pull',
 
1812
            "bzr client.", (0, 15))
 
1813
        self.add_hook('post_pull',
1817
1814
            "Called after a pull operation completes. post_pull is called "
1818
1815
            "with a bzrlib.branch.PullResult object and only runs in the "
1819
 
            "bzr client.", (0, 15), None))
1820
 
        self.create_hook(HookPoint('pre_commit',
1821
 
            "Called after a commit is calculated but before it is is "
 
1816
            "bzr client.", (0, 15))
 
1817
        self.add_hook('pre_commit',
 
1818
            "Called after a commit is calculated but before it is "
1822
1819
            "completed. pre_commit is called with (local, master, old_revno, "
1823
1820
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1824
1821
            "). old_revid is NULL_REVISION for the first commit to a branch, "
1826
1823
            "basis revision. hooks MUST NOT modify this delta. "
1827
1824
            " future_tree is an in-memory tree obtained from "
1828
1825
            "CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1829
 
            "tree.", (0,91), None))
1830
 
        self.create_hook(HookPoint('post_commit',
 
1826
            "tree.", (0,91))
 
1827
        self.add_hook('post_commit',
1831
1828
            "Called in the bzr client after a commit has completed. "
1832
1829
            "post_commit is called with (local, master, old_revno, old_revid, "
1833
1830
            "new_revno, new_revid). old_revid is NULL_REVISION for the first "
1834
 
            "commit to a branch.", (0, 15), None))
1835
 
        self.create_hook(HookPoint('post_uncommit',
 
1831
            "commit to a branch.", (0, 15))
 
1832
        self.add_hook('post_uncommit',
1836
1833
            "Called in the bzr client after an uncommit completes. "
1837
1834
            "post_uncommit is called with (local, master, old_revno, "
1838
1835
            "old_revid, new_revno, new_revid) where local is the local branch "
1839
1836
            "or None, master is the target branch, and an empty branch "
1840
 
            "receives new_revno of 0, new_revid of None.", (0, 15), None))
1841
 
        self.create_hook(HookPoint('pre_change_branch_tip',
 
1837
            "receives new_revno of 0, new_revid of None.", (0, 15))
 
1838
        self.add_hook('pre_change_branch_tip',
1842
1839
            "Called in bzr client and server before a change to the tip of a "
1843
1840
            "branch is made. pre_change_branch_tip is called with a "
1844
1841
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1845
 
            "commit, uncommit will all trigger this hook.", (1, 6), None))
1846
 
        self.create_hook(HookPoint('post_change_branch_tip',
 
1842
            "commit, uncommit will all trigger this hook.", (1, 6))
 
1843
        self.add_hook('post_change_branch_tip',
1847
1844
            "Called in bzr client and server after a change to the tip of a "
1848
1845
            "branch is made. post_change_branch_tip is called with a "
1849
1846
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1850
 
            "commit, uncommit will all trigger this hook.", (1, 4), None))
1851
 
        self.create_hook(HookPoint('transform_fallback_location',
 
1847
            "commit, uncommit will all trigger this hook.", (1, 4))
 
1848
        self.add_hook('transform_fallback_location',
1852
1849
            "Called when a stacked branch is activating its fallback "
1853
1850
            "locations. transform_fallback_location is called with (branch, "
1854
1851
            "url), and should return a new url. Returning the same url "
1859
1856
            "fallback locations have not been activated. When there are "
1860
1857
            "multiple hooks installed for transform_fallback_location, "
1861
1858
            "all are called with the url returned from the previous hook."
1862
 
            "The order is however undefined.", (1, 9), None))
1863
 
        self.create_hook(HookPoint('automatic_tag_name',
1864
 
            "Called to determine an automatic tag name for a revision."
 
1859
            "The order is however undefined.", (1, 9))
 
1860
        self.add_hook('automatic_tag_name',
 
1861
            "Called to determine an automatic tag name for a revision. "
1865
1862
            "automatic_tag_name is called with (branch, revision_id) and "
1866
1863
            "should return a tag name or None if no tag name could be "
1867
1864
            "determined. The first non-None tag name returned will be used.",
1868
 
            (2, 2), None))
1869
 
        self.create_hook(HookPoint('post_branch_init',
 
1865
            (2, 2))
 
1866
        self.add_hook('post_branch_init',
1870
1867
            "Called after new branch initialization completes. "
1871
1868
            "post_branch_init is called with a "
1872
1869
            "bzrlib.branch.BranchInitHookParams. "
1873
1870
            "Note that init, branch and checkout (both heavyweight and "
1874
 
            "lightweight) will all trigger this hook.", (2, 2), None))
1875
 
        self.create_hook(HookPoint('post_switch',
 
1871
            "lightweight) will all trigger this hook.", (2, 2))
 
1872
        self.add_hook('post_switch',
1876
1873
            "Called after a checkout switches branch. "
1877
1874
            "post_switch is called with a "
1878
 
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
 
1875
            "bzrlib.branch.SwitchHookParams.", (2, 2))
1879
1876
 
1880
1877
 
1881
1878
 
1958
1955
        return self.__dict__ == other.__dict__
1959
1956
 
1960
1957
    def __repr__(self):
1961
 
        if self.branch:
1962
 
            return "<%s of %s>" % (self.__class__.__name__, self.branch)
1963
 
        else:
1964
 
            return "<%s of format:%s bzrdir:%s>" % (
1965
 
                self.__class__.__name__, self.branch,
1966
 
                self.format, self.bzrdir)
 
1958
        return "<%s of %s>" % (self.__class__.__name__, self.branch)
1967
1959
 
1968
1960
 
1969
1961
class SwitchHookParams(object):
1999
1991
            self.revision_id)
2000
1992
 
2001
1993
 
2002
 
class BzrBranchFormat4(BranchFormat):
2003
 
    """Bzr branch format 4.
2004
 
 
2005
 
    This format has:
2006
 
     - a revision-history file.
2007
 
     - a branch-lock lock file [ to be shared with the bzrdir ]
2008
 
    """
2009
 
 
2010
 
    def get_format_description(self):
2011
 
        """See BranchFormat.get_format_description()."""
2012
 
        return "Branch format 4"
2013
 
 
2014
 
    def initialize(self, a_bzrdir, name=None):
2015
 
        """Create a branch of this format in a_bzrdir."""
2016
 
        utf8_files = [('revision-history', ''),
2017
 
                      ('branch-name', ''),
2018
 
                      ]
2019
 
        return self._initialize_helper(a_bzrdir, utf8_files, name=name,
2020
 
                                       lock_type='branch4', set_format=False)
2021
 
 
2022
 
    def __init__(self):
2023
 
        super(BzrBranchFormat4, self).__init__()
2024
 
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
2025
 
 
2026
 
    def network_name(self):
2027
 
        """The network name for this format is the control dirs disk label."""
2028
 
        return self._matchingbzrdir.get_format_string()
2029
 
 
2030
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2031
 
        """See BranchFormat.open()."""
2032
 
        if not _found:
2033
 
            # we are being called directly and must probe.
2034
 
            raise NotImplementedError
2035
 
        return BzrBranch(_format=self,
2036
 
                         _control_files=a_bzrdir._control_files,
2037
 
                         a_bzrdir=a_bzrdir,
2038
 
                         name=name,
2039
 
                         _repository=a_bzrdir.open_repository())
2040
 
 
2041
 
    def __str__(self):
2042
 
        return "Bazaar-NG branch format 4"
2043
 
 
2044
 
 
2045
1994
class BranchFormatMetadir(BranchFormat):
2046
1995
    """Common logic for meta-dir based branch formats."""
2047
1996
 
2049
1998
        """What class to instantiate on open calls."""
2050
1999
        raise NotImplementedError(self._branch_class)
2051
2000
 
 
2001
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
2002
                           repository=None):
 
2003
        """Initialize a branch in a bzrdir, with specified files
 
2004
 
 
2005
        :param a_bzrdir: The bzrdir to initialize the branch in
 
2006
        :param utf8_files: The files to create as a list of
 
2007
            (filename, content) tuples
 
2008
        :param name: Name of colocated branch to create, if any
 
2009
        :return: a branch in this format
 
2010
        """
 
2011
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
2012
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
 
2013
        control_files = lockable_files.LockableFiles(branch_transport,
 
2014
            'lock', lockdir.LockDir)
 
2015
        control_files.create_lock()
 
2016
        control_files.lock_write()
 
2017
        try:
 
2018
            utf8_files += [('format', self.get_format_string())]
 
2019
            for (filename, content) in utf8_files:
 
2020
                branch_transport.put_bytes(
 
2021
                    filename, content,
 
2022
                    mode=a_bzrdir._get_file_mode())
 
2023
        finally:
 
2024
            control_files.unlock()
 
2025
        branch = self.open(a_bzrdir, name, _found=True,
 
2026
                found_repository=repository)
 
2027
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
2028
        return branch
 
2029
 
2052
2030
    def network_name(self):
2053
2031
        """A simple byte string uniquely identifying this format for RPC calls.
2054
2032
 
2056
2034
        """
2057
2035
        return self.get_format_string()
2058
2036
 
2059
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
2037
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
 
2038
            found_repository=None):
2060
2039
        """See BranchFormat.open()."""
2061
2040
        if not _found:
2062
2041
            format = BranchFormat.find_format(a_bzrdir, name=name)
2067
2046
        try:
2068
2047
            control_files = lockable_files.LockableFiles(transport, 'lock',
2069
2048
                                                         lockdir.LockDir)
 
2049
            if found_repository is None:
 
2050
                found_repository = a_bzrdir.find_repository()
2070
2051
            return self._branch_class()(_format=self,
2071
2052
                              _control_files=control_files,
2072
2053
                              name=name,
2073
2054
                              a_bzrdir=a_bzrdir,
2074
 
                              _repository=a_bzrdir.find_repository(),
 
2055
                              _repository=found_repository,
2075
2056
                              ignore_fallbacks=ignore_fallbacks)
2076
2057
        except errors.NoSuchFile:
2077
2058
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2084
2065
    def supports_tags(self):
2085
2066
        return True
2086
2067
 
 
2068
    def supports_leaving_lock(self):
 
2069
        return True
 
2070
 
2087
2071
 
2088
2072
class BzrBranchFormat5(BranchFormatMetadir):
2089
2073
    """Bzr branch format 5.
2109
2093
        """See BranchFormat.get_format_description()."""
2110
2094
        return "Branch format 5"
2111
2095
 
2112
 
    def initialize(self, a_bzrdir, name=None):
 
2096
    def initialize(self, a_bzrdir, name=None, repository=None):
2113
2097
        """Create a branch of this format in a_bzrdir."""
2114
2098
        utf8_files = [('revision-history', ''),
2115
2099
                      ('branch-name', ''),
2116
2100
                      ]
2117
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2101
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2118
2102
 
2119
2103
    def supports_tags(self):
2120
2104
        return False
2142
2126
        """See BranchFormat.get_format_description()."""
2143
2127
        return "Branch format 6"
2144
2128
 
2145
 
    def initialize(self, a_bzrdir, name=None):
 
2129
    def initialize(self, a_bzrdir, name=None, repository=None):
2146
2130
        """Create a branch of this format in a_bzrdir."""
2147
2131
        utf8_files = [('last-revision', '0 null:\n'),
2148
2132
                      ('branch.conf', ''),
2149
2133
                      ('tags', ''),
2150
2134
                      ]
2151
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2135
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2152
2136
 
2153
2137
    def make_tags(self, branch):
2154
2138
        """See bzrlib.branch.BranchFormat.make_tags()."""
2172
2156
        """See BranchFormat.get_format_description()."""
2173
2157
        return "Branch format 8"
2174
2158
 
2175
 
    def initialize(self, a_bzrdir, name=None):
 
2159
    def initialize(self, a_bzrdir, name=None, repository=None):
2176
2160
        """Create a branch of this format in a_bzrdir."""
2177
2161
        utf8_files = [('last-revision', '0 null:\n'),
2178
2162
                      ('branch.conf', ''),
2179
2163
                      ('tags', ''),
2180
2164
                      ('references', '')
2181
2165
                      ]
2182
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2183
 
 
2184
 
    def __init__(self):
2185
 
        super(BzrBranchFormat8, self).__init__()
2186
 
        self._matchingbzrdir.repository_format = \
2187
 
            RepositoryFormatKnitPack5RichRoot()
 
2166
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2188
2167
 
2189
2168
    def make_tags(self, branch):
2190
2169
        """See bzrlib.branch.BranchFormat.make_tags()."""
2199
2178
    supports_reference_locations = True
2200
2179
 
2201
2180
 
2202
 
class BzrBranchFormat7(BzrBranchFormat8):
 
2181
class BzrBranchFormat7(BranchFormatMetadir):
2203
2182
    """Branch format with last-revision, tags, and a stacked location pointer.
2204
2183
 
2205
2184
    The stacked location pointer is passed down to the repository and requires
2208
2187
    This format was introduced in bzr 1.6.
2209
2188
    """
2210
2189
 
2211
 
    def initialize(self, a_bzrdir, name=None):
 
2190
    def initialize(self, a_bzrdir, name=None, repository=None):
2212
2191
        """Create a branch of this format in a_bzrdir."""
2213
2192
        utf8_files = [('last-revision', '0 null:\n'),
2214
2193
                      ('branch.conf', ''),
2215
2194
                      ('tags', ''),
2216
2195
                      ]
2217
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2196
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2218
2197
 
2219
2198
    def _branch_class(self):
2220
2199
        return BzrBranch7
2230
2209
    def supports_set_append_revisions_only(self):
2231
2210
        return True
2232
2211
 
 
2212
    def supports_stacking(self):
 
2213
        return True
 
2214
 
 
2215
    def make_tags(self, branch):
 
2216
        """See bzrlib.branch.BranchFormat.make_tags()."""
 
2217
        return BasicTags(branch)
 
2218
 
2233
2219
    supports_reference_locations = False
2234
2220
 
2235
2221
 
2262
2248
        transport = a_bzrdir.get_branch_transport(None, name=name)
2263
2249
        location = transport.put_bytes('location', to_branch.base)
2264
2250
 
2265
 
    def initialize(self, a_bzrdir, name=None, target_branch=None):
 
2251
    def initialize(self, a_bzrdir, name=None, target_branch=None,
 
2252
            repository=None):
2266
2253
        """Create a branch of this format in a_bzrdir."""
2267
2254
        if target_branch is None:
2268
2255
            # this format does not implement branch itself, thus the implicit
2296
2283
        return clone
2297
2284
 
2298
2285
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2299
 
             possible_transports=None, ignore_fallbacks=False):
 
2286
             possible_transports=None, ignore_fallbacks=False,
 
2287
             found_repository=None):
2300
2288
        """Return the branch that the branch reference in a_bzrdir points at.
2301
2289
 
2302
2290
        :param a_bzrdir: A BzrDir that contains a branch.
2333
2321
        return result
2334
2322
 
2335
2323
 
 
2324
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
 
2325
    """Branch format registry."""
 
2326
 
 
2327
    def __init__(self, other_registry=None):
 
2328
        super(BranchFormatRegistry, self).__init__(other_registry)
 
2329
        self._default_format = None
 
2330
 
 
2331
    def set_default(self, format):
 
2332
        self._default_format = format
 
2333
 
 
2334
    def get_default(self):
 
2335
        return self._default_format
 
2336
 
 
2337
 
2336
2338
network_format_registry = registry.FormatRegistry()
2337
2339
"""Registry of formats indexed by their network name.
2338
2340
 
2341
2343
BranchFormat.network_name() for more detail.
2342
2344
"""
2343
2345
 
 
2346
format_registry = BranchFormatRegistry(network_format_registry)
 
2347
 
2344
2348
 
2345
2349
# formats which have no format string are not discoverable
2346
2350
# and not independently creatable, so are not registered.
2348
2352
__format6 = BzrBranchFormat6()
2349
2353
__format7 = BzrBranchFormat7()
2350
2354
__format8 = BzrBranchFormat8()
2351
 
BranchFormat.register_format(__format5)
2352
 
BranchFormat.register_format(BranchReferenceFormat())
2353
 
BranchFormat.register_format(__format6)
2354
 
BranchFormat.register_format(__format7)
2355
 
BranchFormat.register_format(__format8)
2356
 
BranchFormat.set_default_format(__format7)
2357
 
_legacy_formats = [BzrBranchFormat4(),
2358
 
    ]
2359
 
network_format_registry.register(
2360
 
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
 
2355
format_registry.register(__format5)
 
2356
format_registry.register(BranchReferenceFormat())
 
2357
format_registry.register(__format6)
 
2358
format_registry.register(__format7)
 
2359
format_registry.register(__format8)
 
2360
format_registry.set_default(__format7)
2361
2361
 
2362
2362
 
2363
2363
class BranchWriteLockResult(LogicalLockResult):
2511
2511
        """See Branch.print_file."""
2512
2512
        return self.repository.print_file(file, revision_id)
2513
2513
 
2514
 
    def _write_revision_history(self, history):
2515
 
        """Factored out of set_revision_history.
2516
 
 
2517
 
        This performs the actual writing to disk.
2518
 
        It is intended to be called by BzrBranch5.set_revision_history."""
2519
 
        self._transport.put_bytes(
2520
 
            'revision-history', '\n'.join(history),
2521
 
            mode=self.bzrdir._get_file_mode())
2522
 
 
2523
 
    @needs_write_lock
2524
 
    def set_revision_history(self, rev_history):
2525
 
        """See Branch.set_revision_history."""
2526
 
        if 'evil' in debug.debug_flags:
2527
 
            mutter_callsite(3, "set_revision_history scales with history.")
2528
 
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2529
 
        for rev_id in rev_history:
2530
 
            check_not_reserved_id(rev_id)
2531
 
        if Branch.hooks['post_change_branch_tip']:
2532
 
            # Don't calculate the last_revision_info() if there are no hooks
2533
 
            # that will use it.
2534
 
            old_revno, old_revid = self.last_revision_info()
2535
 
        if len(rev_history) == 0:
2536
 
            revid = _mod_revision.NULL_REVISION
2537
 
        else:
2538
 
            revid = rev_history[-1]
2539
 
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2540
 
        self._write_revision_history(rev_history)
2541
 
        self._clear_cached_state()
2542
 
        self._cache_revision_history(rev_history)
2543
 
        for hook in Branch.hooks['set_rh']:
2544
 
            hook(self, rev_history)
2545
 
        if Branch.hooks['post_change_branch_tip']:
2546
 
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2547
 
 
2548
 
    def _synchronize_history(self, destination, revision_id):
2549
 
        """Synchronize last revision and revision history between branches.
2550
 
 
2551
 
        This version is most efficient when the destination is also a
2552
 
        BzrBranch5, but works for BzrBranch6 as long as the revision
2553
 
        history is the true lefthand parent history, and all of the revisions
2554
 
        are in the destination's repository.  If not, set_revision_history
2555
 
        will fail.
2556
 
 
2557
 
        :param destination: The branch to copy the history into
2558
 
        :param revision_id: The revision-id to truncate history at.  May
2559
 
          be None to copy complete history.
2560
 
        """
2561
 
        if not isinstance(destination._format, BzrBranchFormat5):
2562
 
            super(BzrBranch, self)._synchronize_history(
2563
 
                destination, revision_id)
2564
 
            return
2565
 
        if revision_id == _mod_revision.NULL_REVISION:
2566
 
            new_history = []
2567
 
        else:
2568
 
            new_history = self.revision_history()
2569
 
        if revision_id is not None and new_history != []:
2570
 
            try:
2571
 
                new_history = new_history[:new_history.index(revision_id) + 1]
2572
 
            except ValueError:
2573
 
                rev = self.repository.get_revision(revision_id)
2574
 
                new_history = rev.get_history(self.repository)[1:]
2575
 
        destination.set_revision_history(new_history)
2576
 
 
2577
2514
    @needs_write_lock
2578
2515
    def set_last_revision_info(self, revno, revision_id):
2579
 
        """Set the last revision of this branch.
2580
 
 
2581
 
        The caller is responsible for checking that the revno is correct
2582
 
        for this revision id.
2583
 
 
2584
 
        It may be possible to set the branch last revision to an id not
2585
 
        present in the repository.  However, branches can also be
2586
 
        configured to check constraints on history, in which case this may not
2587
 
        be permitted.
2588
 
        """
 
2516
        if not revision_id or not isinstance(revision_id, basestring):
 
2517
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2589
2518
        revision_id = _mod_revision.ensure_null(revision_id)
2590
 
        # this old format stores the full history, but this api doesn't
2591
 
        # provide it, so we must generate, and might as well check it's
2592
 
        # correct
2593
 
        history = self._lefthand_history(revision_id)
2594
 
        if len(history) != revno:
2595
 
            raise AssertionError('%d != %d' % (len(history), revno))
2596
 
        self.set_revision_history(history)
2597
 
 
2598
 
    def _gen_revision_history(self):
2599
 
        history = self._transport.get_bytes('revision-history').split('\n')
2600
 
        if history[-1:] == ['']:
2601
 
            # There shouldn't be a trailing newline, but just in case.
2602
 
            history.pop()
2603
 
        return history
2604
 
 
2605
 
    @needs_write_lock
2606
 
    def generate_revision_history(self, revision_id, last_rev=None,
2607
 
        other_branch=None):
2608
 
        """Create a new revision history that will finish with revision_id.
2609
 
 
2610
 
        :param revision_id: the new tip to use.
2611
 
        :param last_rev: The previous last_revision. If not None, then this
2612
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
2613
 
        :param other_branch: The other branch that DivergedBranches should
2614
 
            raise with respect to.
2615
 
        """
2616
 
        self.set_revision_history(self._lefthand_history(revision_id,
2617
 
            last_rev, other_branch))
 
2519
        old_revno, old_revid = self.last_revision_info()
 
2520
        if self._get_append_revisions_only():
 
2521
            self._check_history_violation(revision_id)
 
2522
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
 
2523
        self._write_last_revision_info(revno, revision_id)
 
2524
        self._clear_cached_state()
 
2525
        self._last_revision_info_cache = revno, revision_id
 
2526
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2618
2527
 
2619
2528
    def basis_tree(self):
2620
2529
        """See Branch.basis_tree."""
2629
2538
                pass
2630
2539
        return None
2631
2540
 
2632
 
    def _basic_push(self, target, overwrite, stop_revision):
2633
 
        """Basic implementation of push without bound branches or hooks.
2634
 
 
2635
 
        Must be called with source read locked and target write locked.
2636
 
        """
2637
 
        result = BranchPushResult()
2638
 
        result.source_branch = self
2639
 
        result.target_branch = target
2640
 
        result.old_revno, result.old_revid = target.last_revision_info()
2641
 
        self.update_references(target)
2642
 
        if result.old_revid != self.last_revision():
2643
 
            # We assume that during 'push' this repository is closer than
2644
 
            # the target.
2645
 
            graph = self.repository.get_graph(target.repository)
2646
 
            target.update_revisions(self, stop_revision,
2647
 
                overwrite=overwrite, graph=graph)
2648
 
        if self._push_should_merge_tags():
2649
 
            result.tag_conflicts = self.tags.merge_to(target.tags,
2650
 
                overwrite)
2651
 
        result.new_revno, result.new_revid = target.last_revision_info()
2652
 
        return result
2653
 
 
2654
2541
    def get_stacked_on_url(self):
2655
2542
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2656
2543
 
2667
2554
            self._transport.put_bytes('parent', url + '\n',
2668
2555
                mode=self.bzrdir._get_file_mode())
2669
2556
 
2670
 
 
2671
 
class BzrBranch5(BzrBranch):
2672
 
    """A format 5 branch. This supports new features over plain branches.
2673
 
 
2674
 
    It has support for a master_branch which is the data for bound branches.
2675
 
    """
2676
 
 
2677
 
    def get_bound_location(self):
2678
 
        try:
2679
 
            return self._transport.get_bytes('bound')[:-1]
2680
 
        except errors.NoSuchFile:
2681
 
            return None
2682
 
 
2683
 
    @needs_read_lock
2684
 
    def get_master_branch(self, possible_transports=None):
2685
 
        """Return the branch we are bound to.
2686
 
 
2687
 
        :return: Either a Branch, or None
2688
 
 
2689
 
        This could memoise the branch, but if thats done
2690
 
        it must be revalidated on each new lock.
2691
 
        So for now we just don't memoise it.
2692
 
        # RBC 20060304 review this decision.
2693
 
        """
2694
 
        bound_loc = self.get_bound_location()
2695
 
        if not bound_loc:
2696
 
            return None
2697
 
        try:
2698
 
            return Branch.open(bound_loc,
2699
 
                               possible_transports=possible_transports)
2700
 
        except (errors.NotBranchError, errors.ConnectionError), e:
2701
 
            raise errors.BoundBranchConnectionFailure(
2702
 
                    self, bound_loc, e)
2703
 
 
2704
2557
    @needs_write_lock
2705
 
    def set_bound_location(self, location):
2706
 
        """Set the target where this branch is bound to.
2707
 
 
2708
 
        :param location: URL to the target branch
2709
 
        """
2710
 
        if location:
2711
 
            self._transport.put_bytes('bound', location+'\n',
2712
 
                mode=self.bzrdir._get_file_mode())
2713
 
        else:
2714
 
            try:
2715
 
                self._transport.delete('bound')
2716
 
            except errors.NoSuchFile:
2717
 
                return False
2718
 
            return True
 
2558
    def unbind(self):
 
2559
        """If bound, unbind"""
 
2560
        return self.set_bound_location(None)
2719
2561
 
2720
2562
    @needs_write_lock
2721
2563
    def bind(self, other):
2743
2585
        # history around
2744
2586
        self.set_bound_location(other.base)
2745
2587
 
 
2588
    def get_bound_location(self):
 
2589
        try:
 
2590
            return self._transport.get_bytes('bound')[:-1]
 
2591
        except errors.NoSuchFile:
 
2592
            return None
 
2593
 
 
2594
    @needs_read_lock
 
2595
    def get_master_branch(self, possible_transports=None):
 
2596
        """Return the branch we are bound to.
 
2597
 
 
2598
        :return: Either a Branch, or None
 
2599
        """
 
2600
        if self._master_branch_cache is None:
 
2601
            self._master_branch_cache = self._get_master_branch(
 
2602
                possible_transports)
 
2603
        return self._master_branch_cache
 
2604
 
 
2605
    def _get_master_branch(self, possible_transports):
 
2606
        bound_loc = self.get_bound_location()
 
2607
        if not bound_loc:
 
2608
            return None
 
2609
        try:
 
2610
            return Branch.open(bound_loc,
 
2611
                               possible_transports=possible_transports)
 
2612
        except (errors.NotBranchError, errors.ConnectionError), e:
 
2613
            raise errors.BoundBranchConnectionFailure(
 
2614
                    self, bound_loc, e)
 
2615
 
2746
2616
    @needs_write_lock
2747
 
    def unbind(self):
2748
 
        """If bound, unbind"""
2749
 
        return self.set_bound_location(None)
 
2617
    def set_bound_location(self, location):
 
2618
        """Set the target where this branch is bound to.
 
2619
 
 
2620
        :param location: URL to the target branch
 
2621
        """
 
2622
        self._master_branch_cache = None
 
2623
        if location:
 
2624
            self._transport.put_bytes('bound', location+'\n',
 
2625
                mode=self.bzrdir._get_file_mode())
 
2626
        else:
 
2627
            try:
 
2628
                self._transport.delete('bound')
 
2629
            except errors.NoSuchFile:
 
2630
                return False
 
2631
            return True
2750
2632
 
2751
2633
    @needs_write_lock
2752
2634
    def update(self, possible_transports=None):
2765
2647
            return old_tip
2766
2648
        return None
2767
2649
 
2768
 
 
2769
 
class BzrBranch8(BzrBranch5):
 
2650
    def _read_last_revision_info(self):
 
2651
        revision_string = self._transport.get_bytes('last-revision')
 
2652
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
 
2653
        revision_id = cache_utf8.get_cached_utf8(revision_id)
 
2654
        revno = int(revno)
 
2655
        return revno, revision_id
 
2656
 
 
2657
    def _write_last_revision_info(self, revno, revision_id):
 
2658
        """Simply write out the revision id, with no checks.
 
2659
 
 
2660
        Use set_last_revision_info to perform this safely.
 
2661
 
 
2662
        Does not update the revision_history cache.
 
2663
        """
 
2664
        revision_id = _mod_revision.ensure_null(revision_id)
 
2665
        out_string = '%d %s\n' % (revno, revision_id)
 
2666
        self._transport.put_bytes('last-revision', out_string,
 
2667
            mode=self.bzrdir._get_file_mode())
 
2668
 
 
2669
 
 
2670
class FullHistoryBzrBranch(BzrBranch):
 
2671
    """Bzr branch which contains the full revision history."""
 
2672
 
 
2673
    @needs_write_lock
 
2674
    def set_last_revision_info(self, revno, revision_id):
 
2675
        if not revision_id or not isinstance(revision_id, basestring):
 
2676
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2677
        revision_id = _mod_revision.ensure_null(revision_id)
 
2678
        # this old format stores the full history, but this api doesn't
 
2679
        # provide it, so we must generate, and might as well check it's
 
2680
        # correct
 
2681
        history = self._lefthand_history(revision_id)
 
2682
        if len(history) != revno:
 
2683
            raise AssertionError('%d != %d' % (len(history), revno))
 
2684
        self._set_revision_history(history)
 
2685
 
 
2686
    def _read_last_revision_info(self):
 
2687
        rh = self.revision_history()
 
2688
        revno = len(rh)
 
2689
        if revno:
 
2690
            return (revno, rh[-1])
 
2691
        else:
 
2692
            return (0, _mod_revision.NULL_REVISION)
 
2693
 
 
2694
    @deprecated_method(deprecated_in((2, 4, 0)))
 
2695
    @needs_write_lock
 
2696
    def set_revision_history(self, rev_history):
 
2697
        """See Branch.set_revision_history."""
 
2698
        self._set_revision_history(rev_history)
 
2699
 
 
2700
    def _set_revision_history(self, rev_history):
 
2701
        if 'evil' in debug.debug_flags:
 
2702
            mutter_callsite(3, "set_revision_history scales with history.")
 
2703
        check_not_reserved_id = _mod_revision.check_not_reserved_id
 
2704
        for rev_id in rev_history:
 
2705
            check_not_reserved_id(rev_id)
 
2706
        if Branch.hooks['post_change_branch_tip']:
 
2707
            # Don't calculate the last_revision_info() if there are no hooks
 
2708
            # that will use it.
 
2709
            old_revno, old_revid = self.last_revision_info()
 
2710
        if len(rev_history) == 0:
 
2711
            revid = _mod_revision.NULL_REVISION
 
2712
        else:
 
2713
            revid = rev_history[-1]
 
2714
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
 
2715
        self._write_revision_history(rev_history)
 
2716
        self._clear_cached_state()
 
2717
        self._cache_revision_history(rev_history)
 
2718
        for hook in Branch.hooks['set_rh']:
 
2719
            hook(self, rev_history)
 
2720
        if Branch.hooks['post_change_branch_tip']:
 
2721
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2722
 
 
2723
    def _write_revision_history(self, history):
 
2724
        """Factored out of set_revision_history.
 
2725
 
 
2726
        This performs the actual writing to disk.
 
2727
        It is intended to be called by set_revision_history."""
 
2728
        self._transport.put_bytes(
 
2729
            'revision-history', '\n'.join(history),
 
2730
            mode=self.bzrdir._get_file_mode())
 
2731
 
 
2732
    def _gen_revision_history(self):
 
2733
        history = self._transport.get_bytes('revision-history').split('\n')
 
2734
        if history[-1:] == ['']:
 
2735
            # There shouldn't be a trailing newline, but just in case.
 
2736
            history.pop()
 
2737
        return history
 
2738
 
 
2739
    def _synchronize_history(self, destination, revision_id):
 
2740
        if not isinstance(destination, FullHistoryBzrBranch):
 
2741
            super(BzrBranch, self)._synchronize_history(
 
2742
                destination, revision_id)
 
2743
            return
 
2744
        if revision_id == _mod_revision.NULL_REVISION:
 
2745
            new_history = []
 
2746
        else:
 
2747
            new_history = self.revision_history()
 
2748
        if revision_id is not None and new_history != []:
 
2749
            try:
 
2750
                new_history = new_history[:new_history.index(revision_id) + 1]
 
2751
            except ValueError:
 
2752
                rev = self.repository.get_revision(revision_id)
 
2753
                new_history = rev.get_history(self.repository)[1:]
 
2754
        destination._set_revision_history(new_history)
 
2755
 
 
2756
    @needs_write_lock
 
2757
    def generate_revision_history(self, revision_id, last_rev=None,
 
2758
        other_branch=None):
 
2759
        """Create a new revision history that will finish with revision_id.
 
2760
 
 
2761
        :param revision_id: the new tip to use.
 
2762
        :param last_rev: The previous last_revision. If not None, then this
 
2763
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
2764
        :param other_branch: The other branch that DivergedBranches should
 
2765
            raise with respect to.
 
2766
        """
 
2767
        self._set_revision_history(self._lefthand_history(revision_id,
 
2768
            last_rev, other_branch))
 
2769
 
 
2770
 
 
2771
class BzrBranch5(FullHistoryBzrBranch):
 
2772
    """A format 5 branch. This supports new features over plain branches.
 
2773
 
 
2774
    It has support for a master_branch which is the data for bound branches.
 
2775
    """
 
2776
 
 
2777
 
 
2778
class BzrBranch8(BzrBranch):
2770
2779
    """A branch that stores tree-reference locations."""
2771
2780
 
2772
2781
    def _open_hook(self):
2798
2807
        self._last_revision_info_cache = None
2799
2808
        self._reference_info = None
2800
2809
 
2801
 
    def _last_revision_info(self):
2802
 
        revision_string = self._transport.get_bytes('last-revision')
2803
 
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2804
 
        revision_id = cache_utf8.get_cached_utf8(revision_id)
2805
 
        revno = int(revno)
2806
 
        return revno, revision_id
2807
 
 
2808
 
    def _write_last_revision_info(self, revno, revision_id):
2809
 
        """Simply write out the revision id, with no checks.
2810
 
 
2811
 
        Use set_last_revision_info to perform this safely.
2812
 
 
2813
 
        Does not update the revision_history cache.
2814
 
        Intended to be called by set_last_revision_info and
2815
 
        _write_revision_history.
2816
 
        """
2817
 
        revision_id = _mod_revision.ensure_null(revision_id)
2818
 
        out_string = '%d %s\n' % (revno, revision_id)
2819
 
        self._transport.put_bytes('last-revision', out_string,
2820
 
            mode=self.bzrdir._get_file_mode())
2821
 
 
2822
 
    @needs_write_lock
2823
 
    def set_last_revision_info(self, revno, revision_id):
2824
 
        revision_id = _mod_revision.ensure_null(revision_id)
2825
 
        old_revno, old_revid = self.last_revision_info()
2826
 
        if self._get_append_revisions_only():
2827
 
            self._check_history_violation(revision_id)
2828
 
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2829
 
        self._write_last_revision_info(revno, revision_id)
2830
 
        self._clear_cached_state()
2831
 
        self._last_revision_info_cache = revno, revision_id
2832
 
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2833
 
 
2834
 
    def _synchronize_history(self, destination, revision_id):
2835
 
        """Synchronize last revision and revision history between branches.
2836
 
 
2837
 
        :see: Branch._synchronize_history
2838
 
        """
2839
 
        # XXX: The base Branch has a fast implementation of this method based
2840
 
        # on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2841
 
        # that uses set_revision_history.  This class inherits from BzrBranch5,
2842
 
        # but wants the fast implementation, so it calls
2843
 
        # Branch._synchronize_history directly.
2844
 
        Branch._synchronize_history(self, destination, revision_id)
2845
 
 
2846
2810
    def _check_history_violation(self, revision_id):
2847
2811
        last_revision = _mod_revision.ensure_null(self.last_revision())
2848
2812
        if _mod_revision.is_null(last_revision):
2857
2821
        self._extend_partial_history(stop_index=last_revno-1)
2858
2822
        return list(reversed(self._partial_revision_history_cache))
2859
2823
 
2860
 
    def _write_revision_history(self, history):
2861
 
        """Factored out of set_revision_history.
2862
 
 
2863
 
        This performs the actual writing to disk, with format-specific checks.
2864
 
        It is intended to be called by BzrBranch5.set_revision_history.
2865
 
        """
2866
 
        if len(history) == 0:
2867
 
            last_revision = 'null:'
2868
 
        else:
2869
 
            if history != self._lefthand_history(history[-1]):
2870
 
                raise errors.NotLefthandHistory(history)
2871
 
            last_revision = history[-1]
2872
 
        if self._get_append_revisions_only():
2873
 
            self._check_history_violation(last_revision)
2874
 
        self._write_last_revision_info(len(history), last_revision)
2875
 
 
2876
2824
    @needs_write_lock
2877
2825
    def _set_parent_location(self, url):
2878
2826
        """Set the parent branch"""
2964
2912
 
2965
2913
    def set_bound_location(self, location):
2966
2914
        """See Branch.set_push_location."""
 
2915
        self._master_branch_cache = None
2967
2916
        result = None
2968
2917
        config = self.get_config()
2969
2918
        if location is None:
3009
2958
        return self.get_config(
3010
2959
            ).get_user_option_as_bool('append_revisions_only')
3011
2960
 
3012
 
    @needs_write_lock
3013
 
    def generate_revision_history(self, revision_id, last_rev=None,
3014
 
                                  other_branch=None):
3015
 
        """See BzrBranch5.generate_revision_history"""
3016
 
        history = self._lefthand_history(revision_id, last_rev, other_branch)
3017
 
        revno = len(history)
3018
 
        self.set_last_revision_info(revno, revision_id)
3019
 
 
3020
2961
    @needs_read_lock
3021
2962
    def get_rev_id(self, revno, history=None):
3022
2963
        """Find the revision id of the specified revno."""
3046
2987
        try:
3047
2988
            index = self._partial_revision_history_cache.index(revision_id)
3048
2989
        except ValueError:
3049
 
            self._extend_partial_history(stop_revision=revision_id)
 
2990
            try:
 
2991
                self._extend_partial_history(stop_revision=revision_id)
 
2992
            except errors.RevisionNotPresent, e:
 
2993
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
3050
2994
            index = len(self._partial_revision_history_cache) - 1
3051
2995
            if self._partial_revision_history_cache[index] != revision_id:
3052
2996
                raise errors.NoSuchRevision(self, revision_id)
3107
3051
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
3108
3052
    """
3109
3053
 
 
3054
    @deprecated_method(deprecated_in((2, 3, 0)))
3110
3055
    def __int__(self):
3111
 
        # DEPRECATED: pull used to return the change in revno
 
3056
        """Return the relative change in revno.
 
3057
 
 
3058
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3059
        """
3112
3060
        return self.new_revno - self.old_revno
3113
3061
 
3114
3062
    def report(self, to_file):
3139
3087
        target, otherwise it will be None.
3140
3088
    """
3141
3089
 
 
3090
    @deprecated_method(deprecated_in((2, 3, 0)))
3142
3091
    def __int__(self):
3143
 
        # DEPRECATED: push used to return the change in revno
 
3092
        """Return the relative change in revno.
 
3093
 
 
3094
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3095
        """
3144
3096
        return self.new_revno - self.old_revno
3145
3097
 
3146
3098
    def report(self, to_file):
3291
3243
        raise NotImplementedError(self.pull)
3292
3244
 
3293
3245
    @needs_write_lock
3294
 
    def update_revisions(self, stop_revision=None, overwrite=False,
3295
 
                         graph=None):
3296
 
        """Pull in new perfect-fit revisions.
3297
 
 
3298
 
        :param stop_revision: Updated until the given revision
3299
 
        :param overwrite: Always set the branch pointer, rather than checking
3300
 
            to see if it is a proper descendant.
3301
 
        :param graph: A Graph object that can be used to query history
3302
 
            information. This can be None.
3303
 
        :return: None
3304
 
        """
3305
 
        raise NotImplementedError(self.update_revisions)
3306
 
 
3307
 
    @needs_write_lock
3308
3246
    def push(self, overwrite=False, stop_revision=None,
3309
3247
             _override_hook_source_branch=None):
3310
3248
        """Mirror the source branch into the target branch.
3322
3260
        """
3323
3261
        raise NotImplementedError(self.copy_content_into)
3324
3262
 
 
3263
    @needs_write_lock
 
3264
    def fetch(self, stop_revision=None):
 
3265
        """Fetch revisions.
 
3266
 
 
3267
        :param stop_revision: Last revision to fetch
 
3268
        """
 
3269
        raise NotImplementedError(self.fetch)
 
3270
 
3325
3271
 
3326
3272
class GenericInterBranch(InterBranch):
3327
3273
    """InterBranch implementation that uses public Branch functions."""
3333
3279
 
3334
3280
    @classmethod
3335
3281
    def _get_branch_formats_to_test(klass):
3336
 
        return [(BranchFormat._default_format, BranchFormat._default_format)]
 
3282
        return [(format_registry.get_default(), format_registry.get_default())]
3337
3283
 
3338
3284
    @classmethod
3339
3285
    def unwrap_format(klass, format):
3340
3286
        if isinstance(format, remote.RemoteBranchFormat):
3341
3287
            format._ensure_real()
3342
3288
            return format._custom_format
3343
 
        return format                                                                                                  
 
3289
        return format
3344
3290
 
3345
3291
    @needs_write_lock
3346
3292
    def copy_content_into(self, revision_id=None):
3362
3308
            self.source.tags.merge_to(self.target.tags)
3363
3309
 
3364
3310
    @needs_write_lock
3365
 
    def update_revisions(self, stop_revision=None, overwrite=False,
3366
 
        graph=None):
3367
 
        """See InterBranch.update_revisions()."""
 
3311
    def fetch(self, stop_revision=None):
 
3312
        if self.target.base == self.source.base:
 
3313
            return (0, [])
 
3314
        self.source.lock_read()
 
3315
        try:
 
3316
            fetch_spec_factory = fetch.FetchSpecFactory()
 
3317
            fetch_spec_factory.source_branch = self.source
 
3318
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
 
3319
            fetch_spec_factory.source_repo = self.source.repository
 
3320
            fetch_spec_factory.target_repo = self.target.repository
 
3321
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
 
3322
            fetch_spec = fetch_spec_factory.make_fetch_spec()
 
3323
            return self.target.repository.fetch(self.source.repository,
 
3324
                fetch_spec=fetch_spec)
 
3325
        finally:
 
3326
            self.source.unlock()
 
3327
 
 
3328
    @needs_write_lock
 
3329
    def _update_revisions(self, stop_revision=None, overwrite=False,
 
3330
            graph=None):
3368
3331
        other_revno, other_last_revision = self.source.last_revision_info()
3369
3332
        stop_revno = None # unknown
3370
3333
        if stop_revision is None:
3381
3344
        # case of having something to pull, and so that the check for
3382
3345
        # already merged can operate on the just fetched graph, which will
3383
3346
        # be cached in memory.
3384
 
        self.target.fetch(self.source, stop_revision)
 
3347
        self.fetch(stop_revision=stop_revision)
3385
3348
        # Check to see if one is an ancestor of the other
3386
3349
        if not overwrite:
3387
3350
            if graph is None:
3415
3378
        if local and not bound_location:
3416
3379
            raise errors.LocalRequiresBoundBranch()
3417
3380
        master_branch = None
3418
 
        if not local and bound_location and self.source.user_url != bound_location:
 
3381
        source_is_master = (self.source.user_url == bound_location)
 
3382
        if not local and bound_location and not source_is_master:
3419
3383
            # not pulling from master, so we need to update master.
3420
3384
            master_branch = self.target.get_master_branch(possible_transports)
3421
3385
            master_branch.lock_write()
3427
3391
            return self._pull(overwrite,
3428
3392
                stop_revision, _hook_master=master_branch,
3429
3393
                run_hooks=run_hooks,
3430
 
                _override_hook_target=_override_hook_target)
 
3394
                _override_hook_target=_override_hook_target,
 
3395
                merge_tags_to_master=not source_is_master)
3431
3396
        finally:
3432
3397
            if master_branch:
3433
3398
                master_branch.unlock()
3454
3419
        finally:
3455
3420
            self.source.unlock()
3456
3421
 
 
3422
    def _basic_push(self, overwrite, stop_revision):
 
3423
        """Basic implementation of push without bound branches or hooks.
 
3424
 
 
3425
        Must be called with source read locked and target write locked.
 
3426
        """
 
3427
        result = BranchPushResult()
 
3428
        result.source_branch = self.source
 
3429
        result.target_branch = self.target
 
3430
        result.old_revno, result.old_revid = self.target.last_revision_info()
 
3431
        self.source.update_references(self.target)
 
3432
        if result.old_revid != stop_revision:
 
3433
            # We assume that during 'push' this repository is closer than
 
3434
            # the target.
 
3435
            graph = self.source.repository.get_graph(self.target.repository)
 
3436
            self._update_revisions(stop_revision, overwrite=overwrite,
 
3437
                    graph=graph)
 
3438
        if self.source._push_should_merge_tags():
 
3439
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3440
                overwrite)
 
3441
        result.new_revno, result.new_revid = self.target.last_revision_info()
 
3442
        return result
 
3443
 
3457
3444
    def _push_with_bound_branches(self, overwrite, stop_revision,
3458
3445
            _override_hook_source_branch=None):
3459
3446
        """Push from source into target, and into target's master if any.
3474
3461
            master_branch.lock_write()
3475
3462
            try:
3476
3463
                # push into the master from the source branch.
3477
 
                self.source._basic_push(master_branch, overwrite, stop_revision)
3478
 
                # and push into the target branch from the source. Note that we
3479
 
                # push from the source branch again, because its considered the
3480
 
                # highest bandwidth repository.
3481
 
                result = self.source._basic_push(self.target, overwrite,
3482
 
                    stop_revision)
 
3464
                master_inter = InterBranch.get(self.source, master_branch)
 
3465
                master_inter._basic_push(overwrite, stop_revision)
 
3466
                # and push into the target branch from the source. Note that
 
3467
                # we push from the source branch again, because it's considered
 
3468
                # the highest bandwidth repository.
 
3469
                result = self._basic_push(overwrite, stop_revision)
3483
3470
                result.master_branch = master_branch
3484
3471
                result.local_branch = self.target
3485
3472
                _run_hooks()
3488
3475
                master_branch.unlock()
3489
3476
        else:
3490
3477
            # no master branch
3491
 
            result = self.source._basic_push(self.target, overwrite,
3492
 
                stop_revision)
 
3478
            result = self._basic_push(overwrite, stop_revision)
3493
3479
            # TODO: Why set master_branch and local_branch if there's no
3494
3480
            # binding?  Maybe cleaner to just leave them unset? -- mbp
3495
3481
            # 20070504
3500
3486
 
3501
3487
    def _pull(self, overwrite=False, stop_revision=None,
3502
3488
             possible_transports=None, _hook_master=None, run_hooks=True,
3503
 
             _override_hook_target=None, local=False):
 
3489
             _override_hook_target=None, local=False,
 
3490
             merge_tags_to_master=True):
3504
3491
        """See Branch.pull.
3505
3492
 
3506
3493
        This function is the core worker, used by GenericInterBranch.pull to
3511
3498
        :param run_hooks: Private parameter - if false, this branch
3512
3499
            is being called because it's the master of the primary branch,
3513
3500
            so it should not run its hooks.
 
3501
            is being called because it's the master of the primary branch,
 
3502
            so it should not run its hooks.
3514
3503
        :param _override_hook_target: Private parameter - set the branch to be
3515
3504
            supplied as the target_branch to pull hooks.
3516
3505
        :param local: Only update the local branch, and not the bound branch.
3535
3524
            # -- JRV20090506
3536
3525
            result.old_revno, result.old_revid = \
3537
3526
                self.target.last_revision_info()
3538
 
            self.target.update_revisions(self.source, stop_revision,
3539
 
                overwrite=overwrite, graph=graph)
 
3527
            self._update_revisions(stop_revision, overwrite=overwrite,
 
3528
                graph=graph)
3540
3529
            # TODO: The old revid should be specified when merging tags, 
3541
3530
            # so a tags implementation that versions tags can only 
3542
3531
            # pull in the most recent changes. -- JRV20090506
3543
3532
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3544
 
                overwrite)
 
3533
                overwrite, ignore_master=not merge_tags_to_master)
3545
3534
            result.new_revno, result.new_revid = self.target.last_revision_info()
3546
3535
            if _hook_master:
3547
3536
                result.master_branch = _hook_master