~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Robert Collins
  • Date: 2010-05-06 07:48:22 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506074822-0bsgf2j4h8jx0xkk
Added ``bzrlib.tests.matchers`` as a place to put matchers, along with
our first in-tree matcher. See the module docstring for details.
(Robert Collins)

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,
29
28
        debug,
30
29
        errors,
31
 
        fetch,
32
 
        graph as _mod_graph,
33
30
        lockdir,
34
31
        lockable_files,
35
 
        remote,
36
32
        repository,
37
33
        revision as _mod_revision,
38
34
        rio,
53
49
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
54
50
from bzrlib.hooks import HookPoint, Hooks
55
51
from bzrlib.inter import InterObject
56
 
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
 
52
from bzrlib.lock import _RelockDebugMixin
57
53
from bzrlib import registry
58
54
from bzrlib.symbol_versioning import (
59
55
    deprecated_in,
67
63
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
68
64
 
69
65
 
70
 
class Branch(controldir.ControlComponent):
 
66
class Branch(bzrdir.ControlComponent):
71
67
    """Branch holding a history of revisions.
72
68
 
73
69
    :ivar base:
94
90
        self._revision_id_to_revno_cache = None
95
91
        self._partial_revision_id_to_revno_cache = {}
96
92
        self._partial_revision_history_cache = []
97
 
        self._tags_bytes = None
98
93
        self._last_revision_info_cache = None
99
94
        self._merge_sorted_revisions_cache = None
100
95
        self._open_hook()
107
102
 
108
103
    def _activate_fallback_location(self, url):
109
104
        """Activate the branch/repository from url as a fallback repository."""
110
 
        for existing_fallback_repo in self.repository._fallback_repositories:
111
 
            if existing_fallback_repo.user_url == url:
112
 
                # This fallback is already configured.  This probably only
113
 
                # happens because BzrDir.sprout is a horrible mess.  To avoid
114
 
                # confusing _unstack we don't add this a second time.
115
 
                mutter('duplicate activation of fallback %r on %r', url, self)
116
 
                return
117
105
        repo = self._get_fallback_repository(url)
118
106
        if repo.has_same_location(self.repository):
119
107
            raise errors.UnstackableLocationError(self.user_url, url)
209
197
        return self.supports_tags() and self.tags.get_tag_dict()
210
198
 
211
199
    def get_config(self):
212
 
        """Get a bzrlib.config.BranchConfig for this Branch.
213
 
 
214
 
        This can then be used to get and set configuration options for the
215
 
        branch.
216
 
 
217
 
        :return: A bzrlib.config.BranchConfig.
218
 
        """
219
200
        return BranchConfig(self)
220
201
 
221
202
    def _get_config(self):
237
218
            possible_transports=[self.bzrdir.root_transport])
238
219
        return a_branch.repository
239
220
 
240
 
    @needs_read_lock
241
221
    def _get_tags_bytes(self):
242
222
        """Get the bytes of a serialised tags dict.
243
223
 
250
230
        :return: The bytes of the tags file.
251
231
        :seealso: Branch._set_tags_bytes.
252
232
        """
253
 
        if self._tags_bytes is None:
254
 
            self._tags_bytes = self._transport.get_bytes('tags')
255
 
        return self._tags_bytes
 
233
        return self._transport.get_bytes('tags')
256
234
 
257
235
    def _get_nick(self, local=False, possible_transports=None):
258
236
        config = self.get_config()
260
238
        if not local and not config.has_explicit_nickname():
261
239
            try:
262
240
                master = self.get_master_branch(possible_transports)
263
 
                if master and self.user_url == master.user_url:
264
 
                    raise errors.RecursiveBind(self.user_url)
265
241
                if master is not None:
266
242
                    # return the master branch value
267
243
                    return master.nick
268
 
            except errors.RecursiveBind, e:
269
 
                raise e
270
244
            except errors.BzrError, e:
271
245
                # Silently fall back to local implicit nick if the master is
272
246
                # unavailable
309
283
        new_history.reverse()
310
284
        return new_history
311
285
 
312
 
    def lock_write(self, token=None):
313
 
        """Lock the branch for write operations.
314
 
 
315
 
        :param token: A token to permit reacquiring a previously held and
316
 
            preserved lock.
317
 
        :return: A BranchWriteLockResult.
318
 
        """
 
286
    def lock_write(self):
319
287
        raise NotImplementedError(self.lock_write)
320
288
 
321
289
    def lock_read(self):
322
 
        """Lock the branch for read operations.
323
 
 
324
 
        :return: A bzrlib.lock.LogicalLockResult.
325
 
        """
326
290
        raise NotImplementedError(self.lock_read)
327
291
 
328
292
    def unlock(self):
662
626
        raise errors.UnsupportedOperation(self.get_reference_info, self)
663
627
 
664
628
    @needs_write_lock
665
 
    def fetch(self, from_branch, last_revision=None, fetch_spec=None):
 
629
    def fetch(self, from_branch, last_revision=None, pb=None):
666
630
        """Copy revisions from from_branch into this branch.
667
631
 
668
632
        :param from_branch: Where to copy from.
669
633
        :param last_revision: What revision to stop at (None for at the end
670
634
                              of the branch.
671
 
        :param fetch_spec: If specified, a SearchResult or
672
 
            PendingAncestryResult that describes which revisions to copy.  This
673
 
            allows copying multiple heads at once.  Mutually exclusive with
674
 
            last_revision.
 
635
        :param pb: An optional progress bar to use.
675
636
        :return: None
676
637
        """
677
 
        if fetch_spec is not None and last_revision is not None:
678
 
            raise AssertionError(
679
 
                "fetch_spec and last_revision are mutually exclusive.")
680
638
        if self.base == from_branch.base:
681
639
            return (0, [])
 
640
        if pb is not None:
 
641
            symbol_versioning.warn(
 
642
                symbol_versioning.deprecated_in((1, 14, 0))
 
643
                % "pb parameter to fetch()")
682
644
        from_branch.lock_read()
683
645
        try:
684
 
            if last_revision is None and fetch_spec is None:
 
646
            if last_revision is None:
685
647
                last_revision = from_branch.last_revision()
686
648
                last_revision = _mod_revision.ensure_null(last_revision)
687
649
            return self.repository.fetch(from_branch.repository,
688
650
                                         revision_id=last_revision,
689
 
                                         fetch_spec=fetch_spec)
 
651
                                         pb=pb)
690
652
        finally:
691
653
            from_branch.unlock()
692
654
 
820
782
            old_repository = self.repository
821
783
            if len(old_repository._fallback_repositories) != 1:
822
784
                raise AssertionError("can't cope with fallback repositories "
823
 
                    "of %r (fallbacks: %r)" % (old_repository,
824
 
                        old_repository._fallback_repositories))
825
 
            # Open the new repository object.
826
 
            # Repositories don't offer an interface to remove fallback
827
 
            # repositories today; take the conceptually simpler option and just
828
 
            # reopen it.  We reopen it starting from the URL so that we
829
 
            # get a separate connection for RemoteRepositories and can
830
 
            # stream from one of them to the other.  This does mean doing
831
 
            # separate SSH connection setup, but unstacking is not a
832
 
            # common operation so it's tolerable.
833
 
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
834
 
            new_repository = new_bzrdir.find_repository()
835
 
            if new_repository._fallback_repositories:
836
 
                raise AssertionError("didn't expect %r to have "
837
 
                    "fallback_repositories"
838
 
                    % (self.repository,))
839
 
            # Replace self.repository with the new repository.
840
 
            # Do our best to transfer the lock state (i.e. lock-tokens and
841
 
            # lock count) of self.repository to the new repository.
842
 
            lock_token = old_repository.lock_write().repository_token
843
 
            self.repository = new_repository
844
 
            if isinstance(self, remote.RemoteBranch):
845
 
                # Remote branches can have a second reference to the old
846
 
                # repository that need to be replaced.
847
 
                if self._real_branch is not None:
848
 
                    self._real_branch.repository = new_repository
849
 
            self.repository.lock_write(token=lock_token)
850
 
            if lock_token is not None:
851
 
                old_repository.leave_lock_in_place()
 
785
                    "of %r" % (self.repository,))
 
786
            # unlock it, including unlocking the fallback
852
787
            old_repository.unlock()
853
 
            if lock_token is not None:
854
 
                # XXX: self.repository.leave_lock_in_place() before this
855
 
                # function will not be preserved.  Fortunately that doesn't
856
 
                # affect the current default format (2a), and would be a
857
 
                # corner-case anyway.
858
 
                #  - Andrew Bennetts, 2010/06/30
859
 
                self.repository.dont_leave_lock_in_place()
860
 
            old_lock_count = 0
861
 
            while True:
862
 
                try:
863
 
                    old_repository.unlock()
864
 
                except errors.LockNotHeld:
865
 
                    break
866
 
                old_lock_count += 1
867
 
            if old_lock_count == 0:
868
 
                raise AssertionError(
869
 
                    'old_repository should have been locked at least once.')
870
 
            for i in range(old_lock_count-1):
 
788
            old_repository.lock_read()
 
789
            try:
 
790
                # Repositories don't offer an interface to remove fallback
 
791
                # repositories today; take the conceptually simpler option and just
 
792
                # reopen it.  We reopen it starting from the URL so that we
 
793
                # get a separate connection for RemoteRepositories and can
 
794
                # stream from one of them to the other.  This does mean doing
 
795
                # separate SSH connection setup, but unstacking is not a
 
796
                # common operation so it's tolerable.
 
797
                new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
 
798
                new_repository = new_bzrdir.find_repository()
 
799
                self.repository = new_repository
 
800
                if self.repository._fallback_repositories:
 
801
                    raise AssertionError("didn't expect %r to have "
 
802
                        "fallback_repositories"
 
803
                        % (self.repository,))
 
804
                # this is not paired with an unlock because it's just restoring
 
805
                # the previous state; the lock's released when set_stacked_on_url
 
806
                # returns
871
807
                self.repository.lock_write()
872
 
            # Fetch from the old repository into the new.
873
 
            old_repository.lock_read()
874
 
            try:
875
808
                # XXX: If you unstack a branch while it has a working tree
876
809
                # with a pending merge, the pending-merged revisions will no
877
810
                # longer be present.  You can (probably) revert and remerge.
878
 
                try:
879
 
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
880
 
                except errors.TagsNotSupported:
881
 
                    tags_to_fetch = set()
882
 
                fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
883
 
                    old_repository, required_ids=[self.last_revision()],
884
 
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
885
 
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
 
811
                #
 
812
                # XXX: This only fetches up to the tip of the repository; it
 
813
                # doesn't bring across any tags.  That's fairly consistent
 
814
                # with how branch works, but perhaps not ideal.
 
815
                self.repository.fetch(old_repository,
 
816
                    revision_id=self.last_revision(),
 
817
                    find_ghosts=True)
886
818
            finally:
887
819
                old_repository.unlock()
888
820
        finally:
893
825
 
894
826
        :seealso: Branch._get_tags_bytes.
895
827
        """
896
 
        return _run_with_write_locked_target(self, self._set_tags_bytes_locked,
897
 
                bytes)
898
 
 
899
 
    def _set_tags_bytes_locked(self, bytes):
900
 
        self._tags_bytes = bytes
901
 
        return self._transport.put_bytes('tags', bytes)
 
828
        return _run_with_write_locked_target(self, self._transport.put_bytes,
 
829
            'tags', bytes)
902
830
 
903
831
    def _cache_revision_history(self, rev_history):
904
832
        """Set the cached revision history to rev_history.
934
862
        self._merge_sorted_revisions_cache = None
935
863
        self._partial_revision_history_cache = []
936
864
        self._partial_revision_id_to_revno_cache = {}
937
 
        self._tags_bytes = None
938
865
 
939
866
    def _gen_revision_history(self):
940
867
        """Return sequence of revision hashes on to this branch.
1001
928
        else:
1002
929
            return (0, _mod_revision.NULL_REVISION)
1003
930
 
 
931
    @deprecated_method(deprecated_in((1, 6, 0)))
 
932
    def missing_revisions(self, other, stop_revision=None):
 
933
        """Return a list of new revisions that would perfectly fit.
 
934
 
 
935
        If self and other have not diverged, return a list of the revisions
 
936
        present in other, but missing from self.
 
937
        """
 
938
        self_history = self.revision_history()
 
939
        self_len = len(self_history)
 
940
        other_history = other.revision_history()
 
941
        other_len = len(other_history)
 
942
        common_index = min(self_len, other_len) -1
 
943
        if common_index >= 0 and \
 
944
            self_history[common_index] != other_history[common_index]:
 
945
            raise errors.DivergedBranches(self, other)
 
946
 
 
947
        if stop_revision is None:
 
948
            stop_revision = other_len
 
949
        else:
 
950
            if stop_revision > other_len:
 
951
                raise errors.NoSuchRevision(self, stop_revision)
 
952
        return other_history[self_len:stop_revision]
 
953
 
 
954
    @needs_write_lock
1004
955
    def update_revisions(self, other, stop_revision=None, overwrite=False,
1005
 
                         graph=None, fetch_tags=True):
 
956
                         graph=None):
1006
957
        """Pull in new perfect-fit revisions.
1007
958
 
1008
959
        :param other: Another Branch to pull from
1011
962
            to see if it is a proper descendant.
1012
963
        :param graph: A Graph object that can be used to query history
1013
964
            information. This can be None.
1014
 
        :param fetch_tags: Flag that specifies if tags from other should be
1015
 
            fetched too.
1016
965
        :return: None
1017
966
        """
1018
967
        return InterBranch.get(other, self).update_revisions(stop_revision,
1019
 
            overwrite, graph, fetch_tags=fetch_tags)
 
968
            overwrite, graph)
1020
969
 
1021
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1022
970
    def import_last_revision_info(self, source_repo, revno, revid):
1023
971
        """Set the last revision info, importing from another repo if necessary.
1024
972
 
 
973
        This is used by the bound branch code to upload a revision to
 
974
        the master branch first before updating the tip of the local branch.
 
975
 
1025
976
        :param source_repo: Source repository to optionally fetch from
1026
977
        :param revno: Revision number of the new tip
1027
978
        :param revid: Revision id of the new tip
1030
981
            self.repository.fetch(source_repo, revision_id=revid)
1031
982
        self.set_last_revision_info(revno, revid)
1032
983
 
1033
 
    def import_last_revision_info_and_tags(self, source, revno, revid):
1034
 
        """Set the last revision info, importing from another repo if necessary.
1035
 
 
1036
 
        This is used by the bound branch code to upload a revision to
1037
 
        the master branch first before updating the tip of the local branch.
1038
 
        Revisions referenced by source's tags are also transferred.
1039
 
 
1040
 
        :param source: Source branch to optionally fetch from
1041
 
        :param revno: Revision number of the new tip
1042
 
        :param revid: Revision id of the new tip
1043
 
        """
1044
 
        if not self.repository.has_same_location(source.repository):
1045
 
            try:
1046
 
                tags_to_fetch = set(source.tags.get_reverse_tag_dict())
1047
 
            except errors.TagsNotSupported:
1048
 
                tags_to_fetch = set()
1049
 
            fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
1050
 
                source.repository, [revid],
1051
 
                if_present_ids=tags_to_fetch).execute()
1052
 
            self.repository.fetch(source.repository, fetch_spec=fetch_spec)
1053
 
        self.set_last_revision_info(revno, revid)
1054
 
 
1055
984
    def revision_id_to_revno(self, revision_id):
1056
985
        """Given a revision id, return its revno"""
1057
986
        if _mod_revision.is_null(revision_id):
1077
1006
            self._extend_partial_history(distance_from_last)
1078
1007
        return self._partial_revision_history_cache[distance_from_last]
1079
1008
 
 
1009
    @needs_write_lock
1080
1010
    def pull(self, source, overwrite=False, stop_revision=None,
1081
1011
             possible_transports=None, *args, **kwargs):
1082
1012
        """Mirror source into this branch.
1278
1208
        return result
1279
1209
 
1280
1210
    @needs_read_lock
1281
 
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
1282
 
            repository=None):
 
1211
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
1283
1212
        """Create a new line of development from the branch, into to_bzrdir.
1284
1213
 
1285
1214
        to_bzrdir controls the branch format.
1290
1219
        if (repository_policy is not None and
1291
1220
            repository_policy.requires_stacking()):
1292
1221
            to_bzrdir._format.require_stacking(_skip_repo=True)
1293
 
        result = to_bzrdir.create_branch(repository=repository)
 
1222
        result = to_bzrdir.create_branch()
1294
1223
        result.lock_write()
1295
1224
        try:
1296
1225
            if repository_policy is not None:
1326
1255
                revno = 1
1327
1256
        destination.set_last_revision_info(revno, revision_id)
1328
1257
 
 
1258
    @needs_read_lock
1329
1259
    def copy_content_into(self, destination, revision_id=None):
1330
1260
        """Copy the content of self into destination.
1331
1261
 
1332
1262
        revision_id: if not None, the revision history in the new branch will
1333
1263
                     be truncated to end with revision_id.
1334
1264
        """
1335
 
        return InterBranch.get(self, destination).copy_content_into(
1336
 
            revision_id=revision_id)
 
1265
        self.update_references(destination)
 
1266
        self._synchronize_history(destination, revision_id)
 
1267
        try:
 
1268
            parent = self.get_parent()
 
1269
        except errors.InaccessibleParent, e:
 
1270
            mutter('parent was not accessible to copy: %s', e)
 
1271
        else:
 
1272
            if parent:
 
1273
                destination.set_parent(parent)
 
1274
        if self._push_should_merge_tags():
 
1275
            self.tags.merge_to(destination.tags)
1337
1276
 
1338
1277
    def update_references(self, target):
1339
1278
        if not getattr(self._format, 'supports_reference_locations', False):
1384
1323
        """Return the most suitable metadir for a checkout of this branch.
1385
1324
        Weaves are used if this branch's repository uses weaves.
1386
1325
        """
1387
 
        format = self.repository.bzrdir.checkout_metadir()
1388
 
        format.set_branch_format(self._format)
 
1326
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
 
1327
            from bzrlib.repofmt import weaverepo
 
1328
            format = bzrdir.BzrDirMetaFormat1()
 
1329
            format.repository_format = weaverepo.RepositoryFormat7()
 
1330
        else:
 
1331
            format = self.repository.bzrdir.checkout_metadir()
 
1332
            format.set_branch_format(self._format)
1389
1333
        return format
1390
1334
 
1391
1335
    def create_clone_on_transport(self, to_transport, revision_id=None,
1392
 
        stacked_on=None, create_prefix=False, use_existing_dir=False,
1393
 
        no_tree=None):
 
1336
        stacked_on=None, create_prefix=False, use_existing_dir=False):
1394
1337
        """Create a clone of this branch and its bzrdir.
1395
1338
 
1396
1339
        :param to_transport: The transport to clone onto.
1403
1346
        """
1404
1347
        # XXX: Fix the bzrdir API to allow getting the branch back from the
1405
1348
        # clone call. Or something. 20090224 RBC/spiv.
1406
 
        # XXX: Should this perhaps clone colocated branches as well, 
1407
 
        # rather than just the default branch? 20100319 JRV
1408
1349
        if revision_id is None:
1409
1350
            revision_id = self.last_revision()
1410
1351
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1411
1352
            revision_id=revision_id, stacked_on=stacked_on,
1412
 
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
1413
 
            no_tree=no_tree)
 
1353
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1414
1354
        return dir_to.open_branch()
1415
1355
 
1416
1356
    def create_checkout(self, to_location, revision_id=None,
1531
1471
        else:
1532
1472
            raise AssertionError("invalid heads: %r" % (heads,))
1533
1473
 
1534
 
    def heads_to_fetch(self):
1535
 
        """Return the heads that must and that should be fetched to copy this
1536
 
        branch into another repo.
1537
 
 
1538
 
        :returns: a 2-tuple of (must_fetch, if_present_fetch).  must_fetch is a
1539
 
            set of heads that must be fetched.  if_present_fetch is a set of
1540
 
            heads that must be fetched if present, but no error is necessary if
1541
 
            they are not present.
1542
 
        """
1543
 
        # For bzr native formats must_fetch is just the tip, and if_present_fetch
1544
 
        # are the tags.
1545
 
        must_fetch = set([self.last_revision()])
1546
 
        try:
1547
 
            if_present_fetch = set(self.tags.get_reverse_tag_dict())
1548
 
        except errors.TagsNotSupported:
1549
 
            if_present_fetch = set()
1550
 
        must_fetch.discard(_mod_revision.NULL_REVISION)
1551
 
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
1552
 
        return must_fetch, if_present_fetch
1553
 
 
1554
1474
 
1555
1475
class BranchFormat(object):
1556
1476
    """An encapsulation of the initialization and open routines for a format.
1561
1481
     * an open routine.
1562
1482
 
1563
1483
    Formats are placed in an dict by their format string for reference
1564
 
    during branch opening. It's not required that these be instances, they
 
1484
    during branch opening. Its not required that these be instances, they
1565
1485
    can be classes themselves with class methods - it simply depends on
1566
1486
    whether state is needed for a given format or not.
1567
1487
 
1576
1496
    _formats = {}
1577
1497
    """The known formats."""
1578
1498
 
1579
 
    _extra_formats = []
1580
 
    """Extra formats that can not be part of a metadir."""
1581
 
 
1582
1499
    can_set_append_revisions_only = True
1583
1500
 
1584
1501
    def __eq__(self, other):
1593
1510
        try:
1594
1511
            transport = a_bzrdir.get_branch_transport(None, name=name)
1595
1512
            format_string = transport.get_bytes("format")
1596
 
            format = klass._formats[format_string]
1597
 
            if isinstance(format, MetaDirBranchFormatFactory):
1598
 
                return format()
1599
 
            return format
 
1513
            return klass._formats[format_string]
1600
1514
        except errors.NoSuchFile:
1601
1515
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1602
1516
        except KeyError:
1607
1521
        """Return the current default format."""
1608
1522
        return klass._default_format
1609
1523
 
1610
 
    @classmethod
1611
 
    def get_formats(klass):
1612
 
        """Get all the known formats.
1613
 
 
1614
 
        Warning: This triggers a load of all lazy registered formats: do not
1615
 
        use except when that is desireed.
1616
 
        """
1617
 
        result = []
1618
 
        for fmt in klass._formats.values():
1619
 
            if isinstance(fmt, MetaDirBranchFormatFactory):
1620
 
                fmt = fmt()
1621
 
            result.append(fmt)
1622
 
        return result + klass._extra_formats
1623
 
 
1624
 
    def get_reference(self, a_bzrdir, name=None):
 
1524
    def get_reference(self, a_bzrdir):
1625
1525
        """Get the target reference of the branch in a_bzrdir.
1626
1526
 
1627
1527
        format probing must have been completed before calling
1629
1529
        in a_bzrdir is correct.
1630
1530
 
1631
1531
        :param a_bzrdir: The bzrdir to get the branch data from.
1632
 
        :param name: Name of the colocated branch to fetch
1633
1532
        :return: None if the branch is not a reference branch.
1634
1533
        """
1635
1534
        return None
1636
1535
 
1637
1536
    @classmethod
1638
 
    def set_reference(self, a_bzrdir, name, to_branch):
 
1537
    def set_reference(self, a_bzrdir, to_branch):
1639
1538
        """Set the target reference of the branch in a_bzrdir.
1640
1539
 
1641
1540
        format probing must have been completed before calling
1643
1542
        in a_bzrdir is correct.
1644
1543
 
1645
1544
        :param a_bzrdir: The bzrdir to set the branch reference for.
1646
 
        :param name: Name of colocated branch to set, None for default
1647
1545
        :param to_branch: branch that the checkout is to reference
1648
1546
        """
1649
1547
        raise NotImplementedError(self.set_reference)
1665
1563
            hook(params)
1666
1564
 
1667
1565
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1668
 
                           repository=None, lock_type='metadir',
1669
 
                           set_format=True):
 
1566
                           lock_type='metadir', set_format=True):
1670
1567
        """Initialize a branch in a bzrdir, with specified files
1671
1568
 
1672
1569
        :param a_bzrdir: The bzrdir to initialize the branch in
1706
1603
        finally:
1707
1604
            if lock_taken:
1708
1605
                control_files.unlock()
1709
 
        branch = self.open(a_bzrdir, name, _found=True,
1710
 
                found_repository=repository)
 
1606
        branch = self.open(a_bzrdir, name, _found=True)
1711
1607
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1712
1608
        return branch
1713
1609
 
1714
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
1610
    def initialize(self, a_bzrdir, name=None):
1715
1611
        """Create a branch of this format in a_bzrdir.
1716
1612
        
1717
1613
        :param name: Name of the colocated branch to create.
1751
1647
        """
1752
1648
        raise NotImplementedError(self.network_name)
1753
1649
 
1754
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
1755
 
            found_repository=None):
 
1650
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1756
1651
        """Return the branch object for a_bzrdir
1757
1652
 
1758
1653
        :param a_bzrdir: A BzrDir that contains a branch.
1765
1660
        raise NotImplementedError(self.open)
1766
1661
 
1767
1662
    @classmethod
1768
 
    def register_extra_format(klass, format):
1769
 
        """Register a branch format that can not be part of a metadir.
1770
 
 
1771
 
        This is mainly useful to allow custom branch formats, such as
1772
 
        older Bazaar formats and foreign formats, to be tested
1773
 
        """
1774
 
        klass._extra_formats.append(format)
1775
 
        network_format_registry.register(
1776
 
            format.network_name(), format.__class__)
1777
 
 
1778
 
    @classmethod
1779
1663
    def register_format(klass, format):
1780
 
        """Register a metadir format.
1781
 
        
1782
 
        See MetaDirBranchFormatFactory for the ability to register a format
1783
 
        without loading the code the format needs until it is actually used.
1784
 
        """
 
1664
        """Register a metadir format."""
1785
1665
        klass._formats[format.get_format_string()] = format
1786
1666
        # Metadir formats have a network name of their format string, and get
1787
 
        # registered as factories.
1788
 
        if isinstance(format, MetaDirBranchFormatFactory):
1789
 
            network_format_registry.register(format.get_format_string(), format)
1790
 
        else:
1791
 
            network_format_registry.register(format.get_format_string(),
1792
 
                format.__class__)
 
1667
        # registered as class factories.
 
1668
        network_format_registry.register(format.get_format_string(), format.__class__)
1793
1669
 
1794
1670
    @classmethod
1795
1671
    def set_default_format(klass, format):
1807
1683
    def unregister_format(klass, format):
1808
1684
        del klass._formats[format.get_format_string()]
1809
1685
 
1810
 
    @classmethod
1811
 
    def unregister_extra_format(klass, format):
1812
 
        klass._extra_formats.remove(format)
1813
 
 
1814
1686
    def __str__(self):
1815
1687
        return self.get_format_description().rstrip()
1816
1688
 
1819
1691
        return False  # by default
1820
1692
 
1821
1693
 
1822
 
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1823
 
    """A factory for a BranchFormat object, permitting simple lazy registration.
1824
 
    
1825
 
    While none of the built in BranchFormats are lazy registered yet,
1826
 
    bzrlib.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
1827
 
    use it, and the bzr-loom plugin uses it as well (see
1828
 
    bzrlib.plugins.loom.formats).
1829
 
    """
1830
 
 
1831
 
    def __init__(self, format_string, module_name, member_name):
1832
 
        """Create a MetaDirBranchFormatFactory.
1833
 
 
1834
 
        :param format_string: The format string the format has.
1835
 
        :param module_name: Module to load the format class from.
1836
 
        :param member_name: Attribute name within the module for the format class.
1837
 
        """
1838
 
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
1839
 
        self._format_string = format_string
1840
 
        
1841
 
    def get_format_string(self):
1842
 
        """See BranchFormat.get_format_string."""
1843
 
        return self._format_string
1844
 
 
1845
 
    def __call__(self):
1846
 
        """Used for network_format_registry support."""
1847
 
        return self.get_obj()()
1848
 
 
1849
 
 
1850
1694
class BranchHooks(Hooks):
1851
1695
    """A dictionary mapping hook name to a list of callables for branch hooks.
1852
1696
 
1879
1723
            "with a bzrlib.branch.PullResult object and only runs in the "
1880
1724
            "bzr client.", (0, 15), None))
1881
1725
        self.create_hook(HookPoint('pre_commit',
1882
 
            "Called after a commit is calculated but before it is "
 
1726
            "Called after a commit is calculated but before it is is "
1883
1727
            "completed. pre_commit is called with (local, master, old_revno, "
1884
1728
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1885
1729
            "). old_revid is NULL_REVISION for the first commit to a branch, "
1922
1766
            "all are called with the url returned from the previous hook."
1923
1767
            "The order is however undefined.", (1, 9), None))
1924
1768
        self.create_hook(HookPoint('automatic_tag_name',
1925
 
            "Called to determine an automatic tag name for a revision. "
 
1769
            "Called to determine an automatic tag name for a revision."
1926
1770
            "automatic_tag_name is called with (branch, revision_id) and "
1927
1771
            "should return a tag name or None if no tag name could be "
1928
1772
            "determined. The first non-None tag name returned will be used.",
2019
1863
        return self.__dict__ == other.__dict__
2020
1864
 
2021
1865
    def __repr__(self):
2022
 
        return "<%s of %s>" % (self.__class__.__name__, self.branch)
 
1866
        if self.branch:
 
1867
            return "<%s of %s>" % (self.__class__.__name__, self.branch)
 
1868
        else:
 
1869
            return "<%s of format:%s bzrdir:%s>" % (
 
1870
                self.__class__.__name__, self.branch,
 
1871
                self.format, self.bzrdir)
2023
1872
 
2024
1873
 
2025
1874
class SwitchHookParams(object):
2067
1916
        """See BranchFormat.get_format_description()."""
2068
1917
        return "Branch format 4"
2069
1918
 
2070
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
1919
    def initialize(self, a_bzrdir, name=None):
2071
1920
        """Create a branch of this format in a_bzrdir."""
2072
 
        if repository is not None:
2073
 
            raise NotImplementedError(
2074
 
                "initialize(repository=<not None>) on %r" % (self,))
2075
1921
        utf8_files = [('revision-history', ''),
2076
1922
                      ('branch-name', ''),
2077
1923
                      ]
2086
1932
        """The network name for this format is the control dirs disk label."""
2087
1933
        return self._matchingbzrdir.get_format_string()
2088
1934
 
2089
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2090
 
            found_repository=None):
 
1935
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2091
1936
        """See BranchFormat.open()."""
2092
1937
        if not _found:
2093
1938
            # we are being called directly and must probe.
2094
1939
            raise NotImplementedError
2095
 
        if found_repository is None:
2096
 
            found_repository = a_bzrdir.open_repository()
2097
 
        return BzrBranchPreSplitOut(_format=self,
 
1940
        return BzrBranch(_format=self,
2098
1941
                         _control_files=a_bzrdir._control_files,
2099
1942
                         a_bzrdir=a_bzrdir,
2100
1943
                         name=name,
2101
 
                         _repository=found_repository)
 
1944
                         _repository=a_bzrdir.open_repository())
2102
1945
 
2103
1946
    def __str__(self):
2104
1947
        return "Bazaar-NG branch format 4"
2118
1961
        """
2119
1962
        return self.get_format_string()
2120
1963
 
2121
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2122
 
            found_repository=None):
 
1964
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2123
1965
        """See BranchFormat.open()."""
2124
1966
        if not _found:
2125
1967
            format = BranchFormat.find_format(a_bzrdir, name=name)
2130
1972
        try:
2131
1973
            control_files = lockable_files.LockableFiles(transport, 'lock',
2132
1974
                                                         lockdir.LockDir)
2133
 
            if found_repository is None:
2134
 
                found_repository = a_bzrdir.find_repository()
2135
1975
            return self._branch_class()(_format=self,
2136
1976
                              _control_files=control_files,
2137
1977
                              name=name,
2138
1978
                              a_bzrdir=a_bzrdir,
2139
 
                              _repository=found_repository,
 
1979
                              _repository=a_bzrdir.find_repository(),
2140
1980
                              ignore_fallbacks=ignore_fallbacks)
2141
1981
        except errors.NoSuchFile:
2142
1982
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2174
2014
        """See BranchFormat.get_format_description()."""
2175
2015
        return "Branch format 5"
2176
2016
 
2177
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2017
    def initialize(self, a_bzrdir, name=None):
2178
2018
        """Create a branch of this format in a_bzrdir."""
2179
2019
        utf8_files = [('revision-history', ''),
2180
2020
                      ('branch-name', ''),
2181
2021
                      ]
2182
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2022
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2183
2023
 
2184
2024
    def supports_tags(self):
2185
2025
        return False
2207
2047
        """See BranchFormat.get_format_description()."""
2208
2048
        return "Branch format 6"
2209
2049
 
2210
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2050
    def initialize(self, a_bzrdir, name=None):
2211
2051
        """Create a branch of this format in a_bzrdir."""
2212
2052
        utf8_files = [('last-revision', '0 null:\n'),
2213
2053
                      ('branch.conf', ''),
2214
2054
                      ('tags', ''),
2215
2055
                      ]
2216
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2056
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2217
2057
 
2218
2058
    def make_tags(self, branch):
2219
2059
        """See bzrlib.branch.BranchFormat.make_tags()."""
2237
2077
        """See BranchFormat.get_format_description()."""
2238
2078
        return "Branch format 8"
2239
2079
 
2240
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2080
    def initialize(self, a_bzrdir, name=None):
2241
2081
        """Create a branch of this format in a_bzrdir."""
2242
2082
        utf8_files = [('last-revision', '0 null:\n'),
2243
2083
                      ('branch.conf', ''),
2244
2084
                      ('tags', ''),
2245
2085
                      ('references', '')
2246
2086
                      ]
2247
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2087
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2248
2088
 
2249
2089
    def __init__(self):
2250
2090
        super(BzrBranchFormat8, self).__init__()
2273
2113
    This format was introduced in bzr 1.6.
2274
2114
    """
2275
2115
 
2276
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2116
    def initialize(self, a_bzrdir, name=None):
2277
2117
        """Create a branch of this format in a_bzrdir."""
2278
2118
        utf8_files = [('last-revision', '0 null:\n'),
2279
2119
                      ('branch.conf', ''),
2280
2120
                      ('tags', ''),
2281
2121
                      ]
2282
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2122
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2283
2123
 
2284
2124
    def _branch_class(self):
2285
2125
        return BzrBranch7
2317
2157
        """See BranchFormat.get_format_description()."""
2318
2158
        return "Checkout reference format 1"
2319
2159
 
2320
 
    def get_reference(self, a_bzrdir, name=None):
 
2160
    def get_reference(self, a_bzrdir):
2321
2161
        """See BranchFormat.get_reference()."""
2322
 
        transport = a_bzrdir.get_branch_transport(None, name=name)
 
2162
        transport = a_bzrdir.get_branch_transport(None)
2323
2163
        return transport.get_bytes('location')
2324
2164
 
2325
 
    def set_reference(self, a_bzrdir, name, to_branch):
 
2165
    def set_reference(self, a_bzrdir, to_branch):
2326
2166
        """See BranchFormat.set_reference()."""
2327
 
        transport = a_bzrdir.get_branch_transport(None, name=name)
 
2167
        transport = a_bzrdir.get_branch_transport(None)
2328
2168
        location = transport.put_bytes('location', to_branch.base)
2329
2169
 
2330
 
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2331
 
            repository=None):
 
2170
    def initialize(self, a_bzrdir, name=None, target_branch=None):
2332
2171
        """Create a branch of this format in a_bzrdir."""
2333
2172
        if target_branch is None:
2334
2173
            # this format does not implement branch itself, thus the implicit
2362
2201
        return clone
2363
2202
 
2364
2203
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2365
 
             possible_transports=None, ignore_fallbacks=False,
2366
 
             found_repository=None):
 
2204
             possible_transports=None, ignore_fallbacks=False):
2367
2205
        """Return the branch that the branch reference in a_bzrdir points at.
2368
2206
 
2369
2207
        :param a_bzrdir: A BzrDir that contains a branch.
2383
2221
                raise AssertionError("wrong format %r found for %r" %
2384
2222
                    (format, self))
2385
2223
        if location is None:
2386
 
            location = self.get_reference(a_bzrdir, name)
 
2224
            location = self.get_reference(a_bzrdir)
2387
2225
        real_bzrdir = bzrdir.BzrDir.open(
2388
2226
            location, possible_transports=possible_transports)
2389
2227
        result = real_bzrdir.open_branch(name=name, 
2421
2259
BranchFormat.register_format(__format7)
2422
2260
BranchFormat.register_format(__format8)
2423
2261
BranchFormat.set_default_format(__format7)
2424
 
BranchFormat.register_extra_format(BzrBranchFormat4())
2425
 
 
2426
 
 
2427
 
class BranchWriteLockResult(LogicalLockResult):
2428
 
    """The result of write locking a branch.
2429
 
 
2430
 
    :ivar branch_token: The token obtained from the underlying branch lock, or
2431
 
        None.
2432
 
    :ivar unlock: A callable which will unlock the lock.
2433
 
    """
2434
 
 
2435
 
    def __init__(self, unlock, branch_token):
2436
 
        LogicalLockResult.__init__(self, unlock)
2437
 
        self.branch_token = branch_token
2438
 
 
2439
 
    def __repr__(self):
2440
 
        return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
2441
 
            self.unlock)
 
2262
_legacy_formats = [BzrBranchFormat4(),
 
2263
    ]
 
2264
network_format_registry.register(
 
2265
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2442
2266
 
2443
2267
 
2444
2268
class BzrBranch(Branch, _RelockDebugMixin):
2500
2324
        return self.control_files.is_locked()
2501
2325
 
2502
2326
    def lock_write(self, token=None):
2503
 
        """Lock the branch for write operations.
2504
 
 
2505
 
        :param token: A token to permit reacquiring a previously held and
2506
 
            preserved lock.
2507
 
        :return: A BranchWriteLockResult.
2508
 
        """
2509
2327
        if not self.is_locked():
2510
2328
            self._note_lock('w')
2511
2329
        # All-in-one needs to always unlock/lock.
2517
2335
        else:
2518
2336
            took_lock = False
2519
2337
        try:
2520
 
            return BranchWriteLockResult(self.unlock,
2521
 
                self.control_files.lock_write(token=token))
 
2338
            return self.control_files.lock_write(token=token)
2522
2339
        except:
2523
2340
            if took_lock:
2524
2341
                self.repository.unlock()
2525
2342
            raise
2526
2343
 
2527
2344
    def lock_read(self):
2528
 
        """Lock the branch for read operations.
2529
 
 
2530
 
        :return: A bzrlib.lock.LogicalLockResult.
2531
 
        """
2532
2345
        if not self.is_locked():
2533
2346
            self._note_lock('r')
2534
2347
        # All-in-one needs to always unlock/lock.
2541
2354
            took_lock = False
2542
2355
        try:
2543
2356
            self.control_files.lock_read()
2544
 
            return LogicalLockResult(self.unlock)
2545
2357
        except:
2546
2358
            if took_lock:
2547
2359
                self.repository.unlock()
2703
2515
        result.target_branch = target
2704
2516
        result.old_revno, result.old_revid = target.last_revision_info()
2705
2517
        self.update_references(target)
2706
 
        if result.old_revid != stop_revision:
 
2518
        if result.old_revid != self.last_revision():
2707
2519
            # We assume that during 'push' this repository is closer than
2708
2520
            # the target.
2709
2521
            graph = self.repository.get_graph(target.repository)
2732
2544
                mode=self.bzrdir._get_file_mode())
2733
2545
 
2734
2546
 
2735
 
class BzrBranchPreSplitOut(BzrBranch):
2736
 
 
2737
 
    def _get_checkout_format(self):
2738
 
        """Return the most suitable metadir for a checkout of this branch.
2739
 
        Weaves are used if this branch's repository uses weaves.
2740
 
        """
2741
 
        from bzrlib.repofmt.weaverepo import RepositoryFormat7
2742
 
        from bzrlib.bzrdir import BzrDirMetaFormat1
2743
 
        format = BzrDirMetaFormat1()
2744
 
        format.repository_format = RepositoryFormat7()
2745
 
        return format
2746
 
 
2747
 
 
2748
2547
class BzrBranch5(BzrBranch):
2749
2548
    """A format 5 branch. This supports new features over plain branches.
2750
2549
 
3184
2983
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
3185
2984
    """
3186
2985
 
3187
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3188
2986
    def __int__(self):
3189
 
        """Return the relative change in revno.
3190
 
 
3191
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3192
 
        """
 
2987
        # DEPRECATED: pull used to return the change in revno
3193
2988
        return self.new_revno - self.old_revno
3194
2989
 
3195
2990
    def report(self, to_file):
3220
3015
        target, otherwise it will be None.
3221
3016
    """
3222
3017
 
3223
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3224
3018
    def __int__(self):
3225
 
        """Return the relative change in revno.
3226
 
 
3227
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3228
 
        """
 
3019
        # DEPRECATED: push used to return the change in revno
3229
3020
        return self.new_revno - self.old_revno
3230
3021
 
3231
3022
    def report(self, to_file):
3354
3145
    _optimisers = []
3355
3146
    """The available optimised InterBranch types."""
3356
3147
 
3357
 
    @classmethod
3358
 
    def _get_branch_formats_to_test(klass):
3359
 
        """Return an iterable of format tuples for testing.
3360
 
        
3361
 
        :return: An iterable of (from_format, to_format) to use when testing
3362
 
            this InterBranch class. Each InterBranch class should define this
3363
 
            method itself.
3364
 
        """
3365
 
        raise NotImplementedError(klass._get_branch_formats_to_test)
 
3148
    @staticmethod
 
3149
    def _get_branch_formats_to_test():
 
3150
        """Return a tuple with the Branch formats to use when testing."""
 
3151
        raise NotImplementedError(InterBranch._get_branch_formats_to_test)
3366
3152
 
3367
 
    @needs_write_lock
3368
3153
    def pull(self, overwrite=False, stop_revision=None,
3369
3154
             possible_transports=None, local=False):
3370
3155
        """Mirror source into target branch.
3375
3160
        """
3376
3161
        raise NotImplementedError(self.pull)
3377
3162
 
3378
 
    @needs_write_lock
3379
3163
    def update_revisions(self, stop_revision=None, overwrite=False,
3380
 
                         graph=None, fetch_tags=True):
 
3164
                         graph=None):
3381
3165
        """Pull in new perfect-fit revisions.
3382
3166
 
3383
3167
        :param stop_revision: Updated until the given revision
3385
3169
            to see if it is a proper descendant.
3386
3170
        :param graph: A Graph object that can be used to query history
3387
3171
            information. This can be None.
3388
 
        :param fetch_tags: Flag that specifies if tags from source should be
3389
 
            fetched too.
3390
3172
        :return: None
3391
3173
        """
3392
3174
        raise NotImplementedError(self.update_revisions)
3393
3175
 
3394
 
    @needs_write_lock
3395
3176
    def push(self, overwrite=False, stop_revision=None,
3396
3177
             _override_hook_source_branch=None):
3397
3178
        """Mirror the source branch into the target branch.
3400
3181
        """
3401
3182
        raise NotImplementedError(self.push)
3402
3183
 
3403
 
    @needs_write_lock
3404
 
    def copy_content_into(self, revision_id=None):
3405
 
        """Copy the content of source into target
3406
 
 
3407
 
        revision_id: if not None, the revision history in the new branch will
3408
 
                     be truncated to end with revision_id.
3409
 
        """
3410
 
        raise NotImplementedError(self.copy_content_into)
3411
 
 
3412
3184
 
3413
3185
class GenericInterBranch(InterBranch):
3414
 
    """InterBranch implementation that uses public Branch functions."""
3415
 
 
3416
 
    @classmethod
3417
 
    def is_compatible(klass, source, target):
3418
 
        # GenericBranch uses the public API, so always compatible
3419
 
        return True
3420
 
 
3421
 
    @classmethod
3422
 
    def _get_branch_formats_to_test(klass):
3423
 
        return [(BranchFormat._default_format, BranchFormat._default_format)]
3424
 
 
3425
 
    @classmethod
3426
 
    def unwrap_format(klass, format):
3427
 
        if isinstance(format, remote.RemoteBranchFormat):
3428
 
            format._ensure_real()
3429
 
            return format._custom_format
3430
 
        return format
3431
 
 
3432
 
    @needs_write_lock
3433
 
    def copy_content_into(self, revision_id=None):
3434
 
        """Copy the content of source into target
3435
 
 
3436
 
        revision_id: if not None, the revision history in the new branch will
3437
 
                     be truncated to end with revision_id.
3438
 
        """
3439
 
        self.source.update_references(self.target)
3440
 
        self.source._synchronize_history(self.target, revision_id)
3441
 
        try:
3442
 
            parent = self.source.get_parent()
3443
 
        except errors.InaccessibleParent, e:
3444
 
            mutter('parent was not accessible to copy: %s', e)
3445
 
        else:
3446
 
            if parent:
3447
 
                self.target.set_parent(parent)
3448
 
        if self.source._push_should_merge_tags():
3449
 
            self.source.tags.merge_to(self.target.tags)
3450
 
 
3451
 
    @needs_write_lock
 
3186
    """InterBranch implementation that uses public Branch functions.
 
3187
    """
 
3188
 
 
3189
    @staticmethod
 
3190
    def _get_branch_formats_to_test():
 
3191
        return BranchFormat._default_format, BranchFormat._default_format
 
3192
 
3452
3193
    def update_revisions(self, stop_revision=None, overwrite=False,
3453
 
        graph=None, fetch_tags=True):
 
3194
        graph=None):
3454
3195
        """See InterBranch.update_revisions()."""
3455
 
        other_revno, other_last_revision = self.source.last_revision_info()
3456
 
        stop_revno = None # unknown
3457
 
        if stop_revision is None:
3458
 
            stop_revision = other_last_revision
3459
 
            if _mod_revision.is_null(stop_revision):
3460
 
                # if there are no commits, we're done.
3461
 
                return
3462
 
            stop_revno = other_revno
3463
 
 
3464
 
        # what's the current last revision, before we fetch [and change it
3465
 
        # possibly]
3466
 
        last_rev = _mod_revision.ensure_null(self.target.last_revision())
3467
 
        # we fetch here so that we don't process data twice in the common
3468
 
        # case of having something to pull, and so that the check for
3469
 
        # already merged can operate on the just fetched graph, which will
3470
 
        # be cached in memory.
3471
 
        if fetch_tags:
3472
 
            fetch_spec_factory = fetch.FetchSpecFactory()
3473
 
            fetch_spec_factory.source_branch = self.source
3474
 
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3475
 
            fetch_spec_factory.source_repo = self.source.repository
3476
 
            fetch_spec_factory.target_repo = self.target.repository
3477
 
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3478
 
            fetch_spec = fetch_spec_factory.make_fetch_spec()
3479
 
        else:
3480
 
            fetch_spec = _mod_graph.NotInOtherForRevs(self.target.repository,
3481
 
                self.source.repository, revision_ids=[stop_revision]).execute()
3482
 
        self.target.fetch(self.source, fetch_spec=fetch_spec)
3483
 
        # Check to see if one is an ancestor of the other
3484
 
        if not overwrite:
3485
 
            if graph is None:
3486
 
                graph = self.target.repository.get_graph()
3487
 
            if self.target._check_if_descendant_or_diverged(
3488
 
                    stop_revision, last_rev, graph, self.source):
3489
 
                # stop_revision is a descendant of last_rev, but we aren't
3490
 
                # overwriting, so we're done.
3491
 
                return
3492
 
        if stop_revno is None:
3493
 
            if graph is None:
3494
 
                graph = self.target.repository.get_graph()
3495
 
            this_revno, this_last_revision = \
3496
 
                    self.target.last_revision_info()
3497
 
            stop_revno = graph.find_distance_to_null(stop_revision,
3498
 
                            [(other_last_revision, other_revno),
3499
 
                             (this_last_revision, this_revno)])
3500
 
        self.target.set_last_revision_info(stop_revno, stop_revision)
3501
 
 
3502
 
    @needs_write_lock
 
3196
        self.source.lock_read()
 
3197
        try:
 
3198
            other_revno, other_last_revision = self.source.last_revision_info()
 
3199
            stop_revno = None # unknown
 
3200
            if stop_revision is None:
 
3201
                stop_revision = other_last_revision
 
3202
                if _mod_revision.is_null(stop_revision):
 
3203
                    # if there are no commits, we're done.
 
3204
                    return
 
3205
                stop_revno = other_revno
 
3206
 
 
3207
            # what's the current last revision, before we fetch [and change it
 
3208
            # possibly]
 
3209
            last_rev = _mod_revision.ensure_null(self.target.last_revision())
 
3210
            # we fetch here so that we don't process data twice in the common
 
3211
            # case of having something to pull, and so that the check for
 
3212
            # already merged can operate on the just fetched graph, which will
 
3213
            # be cached in memory.
 
3214
            self.target.fetch(self.source, stop_revision)
 
3215
            # Check to see if one is an ancestor of the other
 
3216
            if not overwrite:
 
3217
                if graph is None:
 
3218
                    graph = self.target.repository.get_graph()
 
3219
                if self.target._check_if_descendant_or_diverged(
 
3220
                        stop_revision, last_rev, graph, self.source):
 
3221
                    # stop_revision is a descendant of last_rev, but we aren't
 
3222
                    # overwriting, so we're done.
 
3223
                    return
 
3224
            if stop_revno is None:
 
3225
                if graph is None:
 
3226
                    graph = self.target.repository.get_graph()
 
3227
                this_revno, this_last_revision = \
 
3228
                        self.target.last_revision_info()
 
3229
                stop_revno = graph.find_distance_to_null(stop_revision,
 
3230
                                [(other_last_revision, other_revno),
 
3231
                                 (this_last_revision, this_revno)])
 
3232
            self.target.set_last_revision_info(stop_revno, stop_revision)
 
3233
        finally:
 
3234
            self.source.unlock()
 
3235
 
3503
3236
    def pull(self, overwrite=False, stop_revision=None,
3504
 
             possible_transports=None, run_hooks=True,
 
3237
             possible_transports=None, _hook_master=None, run_hooks=True,
3505
3238
             _override_hook_target=None, local=False):
3506
 
        """Pull from source into self, updating my master if any.
 
3239
        """See Branch.pull.
3507
3240
 
 
3241
        :param _hook_master: Private parameter - set the branch to
 
3242
            be supplied as the master to pull hooks.
3508
3243
        :param run_hooks: Private parameter - if false, this branch
3509
3244
            is being called because it's the master of the primary branch,
3510
3245
            so it should not run its hooks.
 
3246
        :param _override_hook_target: Private parameter - set the branch to be
 
3247
            supplied as the target_branch to pull hooks.
 
3248
        :param local: Only update the local branch, and not the bound branch.
3511
3249
        """
3512
 
        bound_location = self.target.get_bound_location()
3513
 
        if local and not bound_location:
 
3250
        # This type of branch can't be bound.
 
3251
        if local:
3514
3252
            raise errors.LocalRequiresBoundBranch()
3515
 
        master_branch = None
3516
 
        source_is_master = (self.source.user_url == bound_location)
3517
 
        if not local and bound_location and not source_is_master:
3518
 
            # not pulling from master, so we need to update master.
3519
 
            master_branch = self.target.get_master_branch(possible_transports)
3520
 
            master_branch.lock_write()
 
3253
        result = PullResult()
 
3254
        result.source_branch = self.source
 
3255
        if _override_hook_target is None:
 
3256
            result.target_branch = self.target
 
3257
        else:
 
3258
            result.target_branch = _override_hook_target
 
3259
        self.source.lock_read()
3521
3260
        try:
3522
 
            if master_branch:
3523
 
                # pull from source into master.
3524
 
                master_branch.pull(self.source, overwrite, stop_revision,
3525
 
                    run_hooks=False)
3526
 
            return self._pull(overwrite,
3527
 
                stop_revision, _hook_master=master_branch,
3528
 
                run_hooks=run_hooks,
3529
 
                _override_hook_target=_override_hook_target,
3530
 
                merge_tags_to_master=not source_is_master)
 
3261
            # We assume that during 'pull' the target repository is closer than
 
3262
            # the source one.
 
3263
            self.source.update_references(self.target)
 
3264
            graph = self.target.repository.get_graph(self.source.repository)
 
3265
            # TODO: Branch formats should have a flag that indicates 
 
3266
            # that revno's are expensive, and pull() should honor that flag.
 
3267
            # -- JRV20090506
 
3268
            result.old_revno, result.old_revid = \
 
3269
                self.target.last_revision_info()
 
3270
            self.target.update_revisions(self.source, stop_revision,
 
3271
                overwrite=overwrite, graph=graph)
 
3272
            # TODO: The old revid should be specified when merging tags, 
 
3273
            # so a tags implementation that versions tags can only 
 
3274
            # pull in the most recent changes. -- JRV20090506
 
3275
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3276
                overwrite)
 
3277
            result.new_revno, result.new_revid = self.target.last_revision_info()
 
3278
            if _hook_master:
 
3279
                result.master_branch = _hook_master
 
3280
                result.local_branch = result.target_branch
 
3281
            else:
 
3282
                result.master_branch = result.target_branch
 
3283
                result.local_branch = None
 
3284
            if run_hooks:
 
3285
                for hook in Branch.hooks['post_pull']:
 
3286
                    hook(result)
3531
3287
        finally:
3532
 
            if master_branch:
3533
 
                master_branch.unlock()
 
3288
            self.source.unlock()
 
3289
        return result
3534
3290
 
3535
3291
    def push(self, overwrite=False, stop_revision=None,
3536
3292
             _override_hook_source_branch=None):
3576
3332
                # push into the master from the source branch.
3577
3333
                self.source._basic_push(master_branch, overwrite, stop_revision)
3578
3334
                # and push into the target branch from the source. Note that we
3579
 
                # push from the source branch again, because it's considered the
 
3335
                # push from the source branch again, because its considered the
3580
3336
                # highest bandwidth repository.
3581
3337
                result = self.source._basic_push(self.target, overwrite,
3582
3338
                    stop_revision)
3598
3354
            _run_hooks()
3599
3355
            return result
3600
3356
 
3601
 
    def _pull(self, overwrite=False, stop_revision=None,
3602
 
             possible_transports=None, _hook_master=None, run_hooks=True,
3603
 
             _override_hook_target=None, local=False,
3604
 
             merge_tags_to_master=True):
3605
 
        """See Branch.pull.
3606
 
 
3607
 
        This function is the core worker, used by GenericInterBranch.pull to
3608
 
        avoid duplication when pulling source->master and source->local.
3609
 
 
3610
 
        :param _hook_master: Private parameter - set the branch to
3611
 
            be supplied as the master to pull hooks.
 
3357
    @classmethod
 
3358
    def is_compatible(self, source, target):
 
3359
        # GenericBranch uses the public API, so always compatible
 
3360
        return True
 
3361
 
 
3362
 
 
3363
class InterToBranch5(GenericInterBranch):
 
3364
 
 
3365
    @staticmethod
 
3366
    def _get_branch_formats_to_test():
 
3367
        return BranchFormat._default_format, BzrBranchFormat5()
 
3368
 
 
3369
    def pull(self, overwrite=False, stop_revision=None,
 
3370
             possible_transports=None, run_hooks=True,
 
3371
             _override_hook_target=None, local=False):
 
3372
        """Pull from source into self, updating my master if any.
 
3373
 
3612
3374
        :param run_hooks: Private parameter - if false, this branch
3613
3375
            is being called because it's the master of the primary branch,
3614
3376
            so it should not run its hooks.
3615
 
        :param _override_hook_target: Private parameter - set the branch to be
3616
 
            supplied as the target_branch to pull hooks.
3617
 
        :param local: Only update the local branch, and not the bound branch.
3618
3377
        """
3619
 
        # This type of branch can't be bound.
3620
 
        if local:
 
3378
        bound_location = self.target.get_bound_location()
 
3379
        if local and not bound_location:
3621
3380
            raise errors.LocalRequiresBoundBranch()
3622
 
        result = PullResult()
3623
 
        result.source_branch = self.source
3624
 
        if _override_hook_target is None:
3625
 
            result.target_branch = self.target
3626
 
        else:
3627
 
            result.target_branch = _override_hook_target
3628
 
        self.source.lock_read()
 
3381
        master_branch = None
 
3382
        if not local and bound_location and self.source.user_url != bound_location:
 
3383
            # not pulling from master, so we need to update master.
 
3384
            master_branch = self.target.get_master_branch(possible_transports)
 
3385
            master_branch.lock_write()
3629
3386
        try:
3630
 
            # We assume that during 'pull' the target repository is closer than
3631
 
            # the source one.
3632
 
            self.source.update_references(self.target)
3633
 
            graph = self.target.repository.get_graph(self.source.repository)
3634
 
            # TODO: Branch formats should have a flag that indicates 
3635
 
            # that revno's are expensive, and pull() should honor that flag.
3636
 
            # -- JRV20090506
3637
 
            result.old_revno, result.old_revid = \
3638
 
                self.target.last_revision_info()
3639
 
            self.target.update_revisions(self.source, stop_revision,
3640
 
                overwrite=overwrite, graph=graph)
3641
 
            # TODO: The old revid should be specified when merging tags, 
3642
 
            # so a tags implementation that versions tags can only 
3643
 
            # pull in the most recent changes. -- JRV20090506
3644
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3645
 
                overwrite, ignore_master=not merge_tags_to_master)
3646
 
            result.new_revno, result.new_revid = self.target.last_revision_info()
3647
 
            if _hook_master:
3648
 
                result.master_branch = _hook_master
3649
 
                result.local_branch = result.target_branch
3650
 
            else:
3651
 
                result.master_branch = result.target_branch
3652
 
                result.local_branch = None
3653
 
            if run_hooks:
3654
 
                for hook in Branch.hooks['post_pull']:
3655
 
                    hook(result)
 
3387
            if master_branch:
 
3388
                # pull from source into master.
 
3389
                master_branch.pull(self.source, overwrite, stop_revision,
 
3390
                    run_hooks=False)
 
3391
            return super(InterToBranch5, self).pull(overwrite,
 
3392
                stop_revision, _hook_master=master_branch,
 
3393
                run_hooks=run_hooks,
 
3394
                _override_hook_target=_override_hook_target)
3656
3395
        finally:
3657
 
            self.source.unlock()
3658
 
        return result
 
3396
            if master_branch:
 
3397
                master_branch.unlock()
3659
3398
 
3660
3399
 
3661
3400
InterBranch.register_optimiser(GenericInterBranch)
 
3401
InterBranch.register_optimiser(InterToBranch5)