~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Vincent Ladeuil
  • Date: 2011-02-08 13:56:49 UTC
  • mfrom: (5609.2.12 2.3)
  • mto: This revision was merged to the branch mainline in revision 5652.
  • Revision ID: v.ladeuil+lp@free.fr-20110208135649-5w2ifp3o040h83f3
Merge 2.3 into trunk including fixes for bug #715058 and bug #713258

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
25
25
        bzrdir,
26
26
        cache_utf8,
27
27
        config as _mod_config,
 
28
        controldir,
28
29
        debug,
29
30
        errors,
 
31
        fetch,
 
32
        graph as _mod_graph,
30
33
        lockdir,
31
34
        lockable_files,
 
35
        remote,
32
36
        repository,
33
37
        revision as _mod_revision,
34
38
        rio,
49
53
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
50
54
from bzrlib.hooks import HookPoint, Hooks
51
55
from bzrlib.inter import InterObject
52
 
from bzrlib.lock import _RelockDebugMixin
 
56
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
53
57
from bzrlib import registry
54
58
from bzrlib.symbol_versioning import (
55
59
    deprecated_in,
63
67
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
64
68
 
65
69
 
66
 
class Branch(bzrdir.ControlComponent):
 
70
class Branch(controldir.ControlComponent):
67
71
    """Branch holding a history of revisions.
68
72
 
69
73
    :ivar base:
90
94
        self._revision_id_to_revno_cache = None
91
95
        self._partial_revision_id_to_revno_cache = {}
92
96
        self._partial_revision_history_cache = []
 
97
        self._tags_bytes = None
93
98
        self._last_revision_info_cache = None
94
99
        self._merge_sorted_revisions_cache = None
95
100
        self._open_hook()
102
107
 
103
108
    def _activate_fallback_location(self, url):
104
109
        """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
105
117
        repo = self._get_fallback_repository(url)
106
118
        if repo.has_same_location(self.repository):
107
119
            raise errors.UnstackableLocationError(self.user_url, url)
197
209
        return self.supports_tags() and self.tags.get_tag_dict()
198
210
 
199
211
    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
        """
200
219
        return BranchConfig(self)
201
220
 
202
221
    def _get_config(self):
218
237
            possible_transports=[self.bzrdir.root_transport])
219
238
        return a_branch.repository
220
239
 
 
240
    @needs_read_lock
221
241
    def _get_tags_bytes(self):
222
242
        """Get the bytes of a serialised tags dict.
223
243
 
230
250
        :return: The bytes of the tags file.
231
251
        :seealso: Branch._set_tags_bytes.
232
252
        """
233
 
        return self._transport.get_bytes('tags')
 
253
        if self._tags_bytes is None:
 
254
            self._tags_bytes = self._transport.get_bytes('tags')
 
255
        return self._tags_bytes
234
256
 
235
257
    def _get_nick(self, local=False, possible_transports=None):
236
258
        config = self.get_config()
238
260
        if not local and not config.has_explicit_nickname():
239
261
            try:
240
262
                master = self.get_master_branch(possible_transports)
 
263
                if master and self.user_url == master.user_url:
 
264
                    raise errors.RecursiveBind(self.user_url)
241
265
                if master is not None:
242
266
                    # return the master branch value
243
267
                    return master.nick
 
268
            except errors.RecursiveBind, e:
 
269
                raise e
244
270
            except errors.BzrError, e:
245
271
                # Silently fall back to local implicit nick if the master is
246
272
                # unavailable
295
321
    def lock_read(self):
296
322
        """Lock the branch for read operations.
297
323
 
298
 
        :return: An object with an unlock method which will release the lock
299
 
            obtained.
 
324
        :return: A bzrlib.lock.LogicalLockResult.
300
325
        """
301
326
        raise NotImplementedError(self.lock_read)
302
327
 
637
662
        raise errors.UnsupportedOperation(self.get_reference_info, self)
638
663
 
639
664
    @needs_write_lock
640
 
    def fetch(self, from_branch, last_revision=None, pb=None):
 
665
    def fetch(self, from_branch, last_revision=None, pb=None, fetch_spec=None):
641
666
        """Copy revisions from from_branch into this branch.
642
667
 
643
668
        :param from_branch: Where to copy from.
644
669
        :param last_revision: What revision to stop at (None for at the end
645
670
                              of the branch.
646
671
        :param pb: An optional progress bar to use.
 
672
        :param fetch_spec: If specified, a SearchResult or
 
673
            PendingAncestryResult that describes which revisions to copy.  This
 
674
            allows copying multiple heads at once.  Mutually exclusive with
 
675
            last_revision.
647
676
        :return: None
648
677
        """
 
678
        if fetch_spec is not None and last_revision is not None:
 
679
            raise AssertionError(
 
680
                "fetch_spec and last_revision are mutually exclusive.")
649
681
        if self.base == from_branch.base:
650
682
            return (0, [])
651
683
        if pb is not None:
654
686
                % "pb parameter to fetch()")
655
687
        from_branch.lock_read()
656
688
        try:
657
 
            if last_revision is None:
 
689
            if last_revision is None and fetch_spec is None:
658
690
                last_revision = from_branch.last_revision()
659
691
                last_revision = _mod_revision.ensure_null(last_revision)
660
692
            return self.repository.fetch(from_branch.repository,
661
693
                                         revision_id=last_revision,
662
 
                                         pb=pb)
 
694
                                         pb=pb, fetch_spec=fetch_spec)
663
695
        finally:
664
696
            from_branch.unlock()
665
697
 
793
825
            old_repository = self.repository
794
826
            if len(old_repository._fallback_repositories) != 1:
795
827
                raise AssertionError("can't cope with fallback repositories "
796
 
                    "of %r" % (self.repository,))
797
 
            # unlock it, including unlocking the fallback
 
828
                    "of %r (fallbacks: %r)" % (old_repository,
 
829
                        old_repository._fallback_repositories))
 
830
            # Open the new repository object.
 
831
            # Repositories don't offer an interface to remove fallback
 
832
            # repositories today; take the conceptually simpler option and just
 
833
            # reopen it.  We reopen it starting from the URL so that we
 
834
            # get a separate connection for RemoteRepositories and can
 
835
            # stream from one of them to the other.  This does mean doing
 
836
            # separate SSH connection setup, but unstacking is not a
 
837
            # common operation so it's tolerable.
 
838
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
 
839
            new_repository = new_bzrdir.find_repository()
 
840
            if new_repository._fallback_repositories:
 
841
                raise AssertionError("didn't expect %r to have "
 
842
                    "fallback_repositories"
 
843
                    % (self.repository,))
 
844
            # Replace self.repository with the new repository.
 
845
            # Do our best to transfer the lock state (i.e. lock-tokens and
 
846
            # lock count) of self.repository to the new repository.
 
847
            lock_token = old_repository.lock_write().repository_token
 
848
            self.repository = new_repository
 
849
            if isinstance(self, remote.RemoteBranch):
 
850
                # Remote branches can have a second reference to the old
 
851
                # repository that need to be replaced.
 
852
                if self._real_branch is not None:
 
853
                    self._real_branch.repository = new_repository
 
854
            self.repository.lock_write(token=lock_token)
 
855
            if lock_token is not None:
 
856
                old_repository.leave_lock_in_place()
798
857
            old_repository.unlock()
 
858
            if lock_token is not None:
 
859
                # XXX: self.repository.leave_lock_in_place() before this
 
860
                # function will not be preserved.  Fortunately that doesn't
 
861
                # affect the current default format (2a), and would be a
 
862
                # corner-case anyway.
 
863
                #  - Andrew Bennetts, 2010/06/30
 
864
                self.repository.dont_leave_lock_in_place()
 
865
            old_lock_count = 0
 
866
            while True:
 
867
                try:
 
868
                    old_repository.unlock()
 
869
                except errors.LockNotHeld:
 
870
                    break
 
871
                old_lock_count += 1
 
872
            if old_lock_count == 0:
 
873
                raise AssertionError(
 
874
                    'old_repository should have been locked at least once.')
 
875
            for i in range(old_lock_count-1):
 
876
                self.repository.lock_write()
 
877
            # Fetch from the old repository into the new.
799
878
            old_repository.lock_read()
800
879
            try:
801
 
                # Repositories don't offer an interface to remove fallback
802
 
                # repositories today; take the conceptually simpler option and just
803
 
                # reopen it.  We reopen it starting from the URL so that we
804
 
                # get a separate connection for RemoteRepositories and can
805
 
                # stream from one of them to the other.  This does mean doing
806
 
                # separate SSH connection setup, but unstacking is not a
807
 
                # common operation so it's tolerable.
808
 
                new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
809
 
                new_repository = new_bzrdir.find_repository()
810
 
                self.repository = new_repository
811
 
                if self.repository._fallback_repositories:
812
 
                    raise AssertionError("didn't expect %r to have "
813
 
                        "fallback_repositories"
814
 
                        % (self.repository,))
815
 
                # this is not paired with an unlock because it's just restoring
816
 
                # the previous state; the lock's released when set_stacked_on_url
817
 
                # returns
818
 
                self.repository.lock_write()
819
880
                # XXX: If you unstack a branch while it has a working tree
820
881
                # with a pending merge, the pending-merged revisions will no
821
882
                # longer be present.  You can (probably) revert and remerge.
836
897
 
837
898
        :seealso: Branch._get_tags_bytes.
838
899
        """
839
 
        return _run_with_write_locked_target(self, self._transport.put_bytes,
840
 
            'tags', bytes)
 
900
        return _run_with_write_locked_target(self, self._set_tags_bytes_locked,
 
901
                bytes)
 
902
 
 
903
    def _set_tags_bytes_locked(self, bytes):
 
904
        self._tags_bytes = bytes
 
905
        return self._transport.put_bytes('tags', bytes)
841
906
 
842
907
    def _cache_revision_history(self, rev_history):
843
908
        """Set the cached revision history to rev_history.
873
938
        self._merge_sorted_revisions_cache = None
874
939
        self._partial_revision_history_cache = []
875
940
        self._partial_revision_id_to_revno_cache = {}
 
941
        self._tags_bytes = None
876
942
 
877
943
    def _gen_revision_history(self):
878
944
        """Return sequence of revision hashes on to this branch.
962
1028
                raise errors.NoSuchRevision(self, stop_revision)
963
1029
        return other_history[self_len:stop_revision]
964
1030
 
965
 
    @needs_write_lock
966
1031
    def update_revisions(self, other, stop_revision=None, overwrite=False,
967
 
                         graph=None):
 
1032
                         graph=None, fetch_tags=True):
968
1033
        """Pull in new perfect-fit revisions.
969
1034
 
970
1035
        :param other: Another Branch to pull from
973
1038
            to see if it is a proper descendant.
974
1039
        :param graph: A Graph object that can be used to query history
975
1040
            information. This can be None.
 
1041
        :param fetch_tags: Flag that specifies if tags from other should be
 
1042
            fetched too.
976
1043
        :return: None
977
1044
        """
978
1045
        return InterBranch.get(other, self).update_revisions(stop_revision,
979
 
            overwrite, graph)
 
1046
            overwrite, graph, fetch_tags=fetch_tags)
980
1047
 
 
1048
    @deprecated_method(deprecated_in((2, 4, 0)))
981
1049
    def import_last_revision_info(self, source_repo, revno, revid):
982
1050
        """Set the last revision info, importing from another repo if necessary.
983
1051
 
984
 
        This is used by the bound branch code to upload a revision to
985
 
        the master branch first before updating the tip of the local branch.
986
 
 
987
1052
        :param source_repo: Source repository to optionally fetch from
988
1053
        :param revno: Revision number of the new tip
989
1054
        :param revid: Revision id of the new tip
992
1057
            self.repository.fetch(source_repo, revision_id=revid)
993
1058
        self.set_last_revision_info(revno, revid)
994
1059
 
 
1060
    def import_last_revision_info_and_tags(self, source, revno, revid):
 
1061
        """Set the last revision info, importing from another repo if necessary.
 
1062
 
 
1063
        This is used by the bound branch code to upload a revision to
 
1064
        the master branch first before updating the tip of the local branch.
 
1065
        Revisions referenced by source's tags are also transferred.
 
1066
 
 
1067
        :param source: Source branch to optionally fetch from
 
1068
        :param revno: Revision number of the new tip
 
1069
        :param revid: Revision id of the new tip
 
1070
        """
 
1071
        if not self.repository.has_same_location(source.repository):
 
1072
            try:
 
1073
                tags_to_fetch = set(source.tags.get_reverse_tag_dict())
 
1074
            except errors.TagsNotSupported:
 
1075
                tags_to_fetch = set()
 
1076
            fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
 
1077
                source.repository, [revid],
 
1078
                if_present_ids=tags_to_fetch).execute()
 
1079
            self.repository.fetch(source.repository, fetch_spec=fetch_spec)
 
1080
        self.set_last_revision_info(revno, revid)
 
1081
 
995
1082
    def revision_id_to_revno(self, revision_id):
996
1083
        """Given a revision id, return its revno"""
997
1084
        if _mod_revision.is_null(revision_id):
1017
1104
            self._extend_partial_history(distance_from_last)
1018
1105
        return self._partial_revision_history_cache[distance_from_last]
1019
1106
 
1020
 
    @needs_write_lock
1021
1107
    def pull(self, source, overwrite=False, stop_revision=None,
1022
1108
             possible_transports=None, *args, **kwargs):
1023
1109
        """Mirror source into this branch.
1219
1305
        return result
1220
1306
 
1221
1307
    @needs_read_lock
1222
 
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
 
1308
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
 
1309
            repository=None):
1223
1310
        """Create a new line of development from the branch, into to_bzrdir.
1224
1311
 
1225
1312
        to_bzrdir controls the branch format.
1230
1317
        if (repository_policy is not None and
1231
1318
            repository_policy.requires_stacking()):
1232
1319
            to_bzrdir._format.require_stacking(_skip_repo=True)
1233
 
        result = to_bzrdir.create_branch()
 
1320
        result = to_bzrdir.create_branch(repository=repository)
1234
1321
        result.lock_write()
1235
1322
        try:
1236
1323
            if repository_policy is not None:
1266
1353
                revno = 1
1267
1354
        destination.set_last_revision_info(revno, revision_id)
1268
1355
 
1269
 
    @needs_read_lock
1270
1356
    def copy_content_into(self, destination, revision_id=None):
1271
1357
        """Copy the content of self into destination.
1272
1358
 
1273
1359
        revision_id: if not None, the revision history in the new branch will
1274
1360
                     be truncated to end with revision_id.
1275
1361
        """
1276
 
        self.update_references(destination)
1277
 
        self._synchronize_history(destination, revision_id)
1278
 
        try:
1279
 
            parent = self.get_parent()
1280
 
        except errors.InaccessibleParent, e:
1281
 
            mutter('parent was not accessible to copy: %s', e)
1282
 
        else:
1283
 
            if parent:
1284
 
                destination.set_parent(parent)
1285
 
        if self._push_should_merge_tags():
1286
 
            self.tags.merge_to(destination.tags)
 
1362
        return InterBranch.get(self, destination).copy_content_into(
 
1363
            revision_id=revision_id)
1287
1364
 
1288
1365
    def update_references(self, target):
1289
1366
        if not getattr(self._format, 'supports_reference_locations', False):
1334
1411
        """Return the most suitable metadir for a checkout of this branch.
1335
1412
        Weaves are used if this branch's repository uses weaves.
1336
1413
        """
1337
 
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
1338
 
            from bzrlib.repofmt import weaverepo
1339
 
            format = bzrdir.BzrDirMetaFormat1()
1340
 
            format.repository_format = weaverepo.RepositoryFormat7()
1341
 
        else:
1342
 
            format = self.repository.bzrdir.checkout_metadir()
1343
 
            format.set_branch_format(self._format)
 
1414
        format = self.repository.bzrdir.checkout_metadir()
 
1415
        format.set_branch_format(self._format)
1344
1416
        return format
1345
1417
 
1346
1418
    def create_clone_on_transport(self, to_transport, revision_id=None,
1347
 
        stacked_on=None, create_prefix=False, use_existing_dir=False):
 
1419
        stacked_on=None, create_prefix=False, use_existing_dir=False,
 
1420
        no_tree=None):
1348
1421
        """Create a clone of this branch and its bzrdir.
1349
1422
 
1350
1423
        :param to_transport: The transport to clone onto.
1357
1430
        """
1358
1431
        # XXX: Fix the bzrdir API to allow getting the branch back from the
1359
1432
        # clone call. Or something. 20090224 RBC/spiv.
 
1433
        # XXX: Should this perhaps clone colocated branches as well, 
 
1434
        # rather than just the default branch? 20100319 JRV
1360
1435
        if revision_id is None:
1361
1436
            revision_id = self.last_revision()
1362
1437
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1363
1438
            revision_id=revision_id, stacked_on=stacked_on,
1364
 
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
 
1439
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
 
1440
            no_tree=no_tree)
1365
1441
        return dir_to.open_branch()
1366
1442
 
1367
1443
    def create_checkout(self, to_location, revision_id=None,
1492
1568
     * an open routine.
1493
1569
 
1494
1570
    Formats are placed in an dict by their format string for reference
1495
 
    during branch opening. Its not required that these be instances, they
 
1571
    during branch opening. It's not required that these be instances, they
1496
1572
    can be classes themselves with class methods - it simply depends on
1497
1573
    whether state is needed for a given format or not.
1498
1574
 
1507
1583
    _formats = {}
1508
1584
    """The known formats."""
1509
1585
 
 
1586
    _extra_formats = []
 
1587
    """Extra formats that can not be part of a metadir."""
 
1588
 
1510
1589
    can_set_append_revisions_only = True
1511
1590
 
1512
1591
    def __eq__(self, other):
1521
1600
        try:
1522
1601
            transport = a_bzrdir.get_branch_transport(None, name=name)
1523
1602
            format_string = transport.get_bytes("format")
1524
 
            return klass._formats[format_string]
 
1603
            format = klass._formats[format_string]
 
1604
            if isinstance(format, MetaDirBranchFormatFactory):
 
1605
                return format()
 
1606
            return format
1525
1607
        except errors.NoSuchFile:
1526
1608
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1527
1609
        except KeyError:
1532
1614
        """Return the current default format."""
1533
1615
        return klass._default_format
1534
1616
 
1535
 
    def get_reference(self, a_bzrdir):
 
1617
    @classmethod
 
1618
    def get_formats(klass):
 
1619
        """Get all the known formats.
 
1620
 
 
1621
        Warning: This triggers a load of all lazy registered formats: do not
 
1622
        use except when that is desireed.
 
1623
        """
 
1624
        result = []
 
1625
        for fmt in klass._formats.values():
 
1626
            if isinstance(fmt, MetaDirBranchFormatFactory):
 
1627
                fmt = fmt()
 
1628
            result.append(fmt)
 
1629
        return result + klass._extra_formats
 
1630
 
 
1631
    def get_reference(self, a_bzrdir, name=None):
1536
1632
        """Get the target reference of the branch in a_bzrdir.
1537
1633
 
1538
1634
        format probing must have been completed before calling
1540
1636
        in a_bzrdir is correct.
1541
1637
 
1542
1638
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1639
        :param name: Name of the colocated branch to fetch
1543
1640
        :return: None if the branch is not a reference branch.
1544
1641
        """
1545
1642
        return None
1546
1643
 
1547
1644
    @classmethod
1548
 
    def set_reference(self, a_bzrdir, to_branch):
 
1645
    def set_reference(self, a_bzrdir, name, to_branch):
1549
1646
        """Set the target reference of the branch in a_bzrdir.
1550
1647
 
1551
1648
        format probing must have been completed before calling
1553
1650
        in a_bzrdir is correct.
1554
1651
 
1555
1652
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1653
        :param name: Name of colocated branch to set, None for default
1556
1654
        :param to_branch: branch that the checkout is to reference
1557
1655
        """
1558
1656
        raise NotImplementedError(self.set_reference)
1574
1672
            hook(params)
1575
1673
 
1576
1674
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1577
 
                           lock_type='metadir', set_format=True):
 
1675
                           repository=None, lock_type='metadir',
 
1676
                           set_format=True):
1578
1677
        """Initialize a branch in a bzrdir, with specified files
1579
1678
 
1580
1679
        :param a_bzrdir: The bzrdir to initialize the branch in
1614
1713
        finally:
1615
1714
            if lock_taken:
1616
1715
                control_files.unlock()
1617
 
        branch = self.open(a_bzrdir, name, _found=True)
 
1716
        branch = self.open(a_bzrdir, name, _found=True,
 
1717
                found_repository=repository)
1618
1718
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1619
1719
        return branch
1620
1720
 
1621
 
    def initialize(self, a_bzrdir, name=None):
 
1721
    def initialize(self, a_bzrdir, name=None, repository=None):
1622
1722
        """Create a branch of this format in a_bzrdir.
1623
1723
        
1624
1724
        :param name: Name of the colocated branch to create.
1658
1758
        """
1659
1759
        raise NotImplementedError(self.network_name)
1660
1760
 
1661
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
1761
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
 
1762
            found_repository=None):
1662
1763
        """Return the branch object for a_bzrdir
1663
1764
 
1664
1765
        :param a_bzrdir: A BzrDir that contains a branch.
1671
1772
        raise NotImplementedError(self.open)
1672
1773
 
1673
1774
    @classmethod
 
1775
    def register_extra_format(klass, format):
 
1776
        """Register a branch format that can not be part of a metadir.
 
1777
 
 
1778
        This is mainly useful to allow custom branch formats, such as
 
1779
        older Bazaar formats and foreign formats, to be tested
 
1780
        """
 
1781
        klass._extra_formats.append(format)
 
1782
        network_format_registry.register(
 
1783
            format.network_name(), format.__class__)
 
1784
 
 
1785
    @classmethod
1674
1786
    def register_format(klass, format):
1675
 
        """Register a metadir format."""
 
1787
        """Register a metadir format.
 
1788
        
 
1789
        See MetaDirBranchFormatFactory for the ability to register a format
 
1790
        without loading the code the format needs until it is actually used.
 
1791
        """
1676
1792
        klass._formats[format.get_format_string()] = format
1677
1793
        # Metadir formats have a network name of their format string, and get
1678
 
        # registered as class factories.
1679
 
        network_format_registry.register(format.get_format_string(), format.__class__)
 
1794
        # registered as factories.
 
1795
        if isinstance(format, MetaDirBranchFormatFactory):
 
1796
            network_format_registry.register(format.get_format_string(), format)
 
1797
        else:
 
1798
            network_format_registry.register(format.get_format_string(),
 
1799
                format.__class__)
1680
1800
 
1681
1801
    @classmethod
1682
1802
    def set_default_format(klass, format):
1694
1814
    def unregister_format(klass, format):
1695
1815
        del klass._formats[format.get_format_string()]
1696
1816
 
 
1817
    @classmethod
 
1818
    def unregister_extra_format(klass, format):
 
1819
        klass._extra_formats.remove(format)
 
1820
 
1697
1821
    def __str__(self):
1698
1822
        return self.get_format_description().rstrip()
1699
1823
 
1702
1826
        return False  # by default
1703
1827
 
1704
1828
 
 
1829
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
 
1830
    """A factory for a BranchFormat object, permitting simple lazy registration.
 
1831
    
 
1832
    While none of the built in BranchFormats are lazy registered yet,
 
1833
    bzrlib.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
 
1834
    use it, and the bzr-loom plugin uses it as well (see
 
1835
    bzrlib.plugins.loom.formats).
 
1836
    """
 
1837
 
 
1838
    def __init__(self, format_string, module_name, member_name):
 
1839
        """Create a MetaDirBranchFormatFactory.
 
1840
 
 
1841
        :param format_string: The format string the format has.
 
1842
        :param module_name: Module to load the format class from.
 
1843
        :param member_name: Attribute name within the module for the format class.
 
1844
        """
 
1845
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
 
1846
        self._format_string = format_string
 
1847
        
 
1848
    def get_format_string(self):
 
1849
        """See BranchFormat.get_format_string."""
 
1850
        return self._format_string
 
1851
 
 
1852
    def __call__(self):
 
1853
        """Used for network_format_registry support."""
 
1854
        return self.get_obj()()
 
1855
 
 
1856
 
1705
1857
class BranchHooks(Hooks):
1706
1858
    """A dictionary mapping hook name to a list of callables for branch hooks.
1707
1859
 
1734
1886
            "with a bzrlib.branch.PullResult object and only runs in the "
1735
1887
            "bzr client.", (0, 15), None))
1736
1888
        self.create_hook(HookPoint('pre_commit',
1737
 
            "Called after a commit is calculated but before it is is "
 
1889
            "Called after a commit is calculated but before it is "
1738
1890
            "completed. pre_commit is called with (local, master, old_revno, "
1739
1891
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1740
1892
            "). old_revid is NULL_REVISION for the first commit to a branch, "
1777
1929
            "all are called with the url returned from the previous hook."
1778
1930
            "The order is however undefined.", (1, 9), None))
1779
1931
        self.create_hook(HookPoint('automatic_tag_name',
1780
 
            "Called to determine an automatic tag name for a revision."
 
1932
            "Called to determine an automatic tag name for a revision. "
1781
1933
            "automatic_tag_name is called with (branch, revision_id) and "
1782
1934
            "should return a tag name or None if no tag name could be "
1783
1935
            "determined. The first non-None tag name returned will be used.",
1874
2026
        return self.__dict__ == other.__dict__
1875
2027
 
1876
2028
    def __repr__(self):
1877
 
        if self.branch:
1878
 
            return "<%s of %s>" % (self.__class__.__name__, self.branch)
1879
 
        else:
1880
 
            return "<%s of format:%s bzrdir:%s>" % (
1881
 
                self.__class__.__name__, self.branch,
1882
 
                self.format, self.bzrdir)
 
2029
        return "<%s of %s>" % (self.__class__.__name__, self.branch)
1883
2030
 
1884
2031
 
1885
2032
class SwitchHookParams(object):
1927
2074
        """See BranchFormat.get_format_description()."""
1928
2075
        return "Branch format 4"
1929
2076
 
1930
 
    def initialize(self, a_bzrdir, name=None):
 
2077
    def initialize(self, a_bzrdir, name=None, repository=None):
1931
2078
        """Create a branch of this format in a_bzrdir."""
 
2079
        if repository is not None:
 
2080
            raise NotImplementedError(
 
2081
                "initialize(repository=<not None>) on %r" % (self,))
1932
2082
        utf8_files = [('revision-history', ''),
1933
2083
                      ('branch-name', ''),
1934
2084
                      ]
1943
2093
        """The network name for this format is the control dirs disk label."""
1944
2094
        return self._matchingbzrdir.get_format_string()
1945
2095
 
1946
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
2096
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
 
2097
            found_repository=None):
1947
2098
        """See BranchFormat.open()."""
1948
2099
        if not _found:
1949
2100
            # we are being called directly and must probe.
1950
2101
            raise NotImplementedError
1951
 
        return BzrBranch(_format=self,
 
2102
        if found_repository is None:
 
2103
            found_repository = a_bzrdir.open_repository()
 
2104
        return BzrBranchPreSplitOut(_format=self,
1952
2105
                         _control_files=a_bzrdir._control_files,
1953
2106
                         a_bzrdir=a_bzrdir,
1954
2107
                         name=name,
1955
 
                         _repository=a_bzrdir.open_repository())
 
2108
                         _repository=found_repository)
1956
2109
 
1957
2110
    def __str__(self):
1958
2111
        return "Bazaar-NG branch format 4"
1972
2125
        """
1973
2126
        return self.get_format_string()
1974
2127
 
1975
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
2128
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
 
2129
            found_repository=None):
1976
2130
        """See BranchFormat.open()."""
1977
2131
        if not _found:
1978
2132
            format = BranchFormat.find_format(a_bzrdir, name=name)
1983
2137
        try:
1984
2138
            control_files = lockable_files.LockableFiles(transport, 'lock',
1985
2139
                                                         lockdir.LockDir)
 
2140
            if found_repository is None:
 
2141
                found_repository = a_bzrdir.find_repository()
1986
2142
            return self._branch_class()(_format=self,
1987
2143
                              _control_files=control_files,
1988
2144
                              name=name,
1989
2145
                              a_bzrdir=a_bzrdir,
1990
 
                              _repository=a_bzrdir.find_repository(),
 
2146
                              _repository=found_repository,
1991
2147
                              ignore_fallbacks=ignore_fallbacks)
1992
2148
        except errors.NoSuchFile:
1993
2149
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2025
2181
        """See BranchFormat.get_format_description()."""
2026
2182
        return "Branch format 5"
2027
2183
 
2028
 
    def initialize(self, a_bzrdir, name=None):
 
2184
    def initialize(self, a_bzrdir, name=None, repository=None):
2029
2185
        """Create a branch of this format in a_bzrdir."""
2030
2186
        utf8_files = [('revision-history', ''),
2031
2187
                      ('branch-name', ''),
2032
2188
                      ]
2033
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2189
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2034
2190
 
2035
2191
    def supports_tags(self):
2036
2192
        return False
2058
2214
        """See BranchFormat.get_format_description()."""
2059
2215
        return "Branch format 6"
2060
2216
 
2061
 
    def initialize(self, a_bzrdir, name=None):
 
2217
    def initialize(self, a_bzrdir, name=None, repository=None):
2062
2218
        """Create a branch of this format in a_bzrdir."""
2063
2219
        utf8_files = [('last-revision', '0 null:\n'),
2064
2220
                      ('branch.conf', ''),
2065
2221
                      ('tags', ''),
2066
2222
                      ]
2067
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2223
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2068
2224
 
2069
2225
    def make_tags(self, branch):
2070
2226
        """See bzrlib.branch.BranchFormat.make_tags()."""
2088
2244
        """See BranchFormat.get_format_description()."""
2089
2245
        return "Branch format 8"
2090
2246
 
2091
 
    def initialize(self, a_bzrdir, name=None):
 
2247
    def initialize(self, a_bzrdir, name=None, repository=None):
2092
2248
        """Create a branch of this format in a_bzrdir."""
2093
2249
        utf8_files = [('last-revision', '0 null:\n'),
2094
2250
                      ('branch.conf', ''),
2095
2251
                      ('tags', ''),
2096
2252
                      ('references', '')
2097
2253
                      ]
2098
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2254
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2099
2255
 
2100
2256
    def __init__(self):
2101
2257
        super(BzrBranchFormat8, self).__init__()
2124
2280
    This format was introduced in bzr 1.6.
2125
2281
    """
2126
2282
 
2127
 
    def initialize(self, a_bzrdir, name=None):
 
2283
    def initialize(self, a_bzrdir, name=None, repository=None):
2128
2284
        """Create a branch of this format in a_bzrdir."""
2129
2285
        utf8_files = [('last-revision', '0 null:\n'),
2130
2286
                      ('branch.conf', ''),
2131
2287
                      ('tags', ''),
2132
2288
                      ]
2133
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2289
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2134
2290
 
2135
2291
    def _branch_class(self):
2136
2292
        return BzrBranch7
2168
2324
        """See BranchFormat.get_format_description()."""
2169
2325
        return "Checkout reference format 1"
2170
2326
 
2171
 
    def get_reference(self, a_bzrdir):
 
2327
    def get_reference(self, a_bzrdir, name=None):
2172
2328
        """See BranchFormat.get_reference()."""
2173
 
        transport = a_bzrdir.get_branch_transport(None)
 
2329
        transport = a_bzrdir.get_branch_transport(None, name=name)
2174
2330
        return transport.get_bytes('location')
2175
2331
 
2176
 
    def set_reference(self, a_bzrdir, to_branch):
 
2332
    def set_reference(self, a_bzrdir, name, to_branch):
2177
2333
        """See BranchFormat.set_reference()."""
2178
 
        transport = a_bzrdir.get_branch_transport(None)
 
2334
        transport = a_bzrdir.get_branch_transport(None, name=name)
2179
2335
        location = transport.put_bytes('location', to_branch.base)
2180
2336
 
2181
 
    def initialize(self, a_bzrdir, name=None, target_branch=None):
 
2337
    def initialize(self, a_bzrdir, name=None, target_branch=None,
 
2338
            repository=None):
2182
2339
        """Create a branch of this format in a_bzrdir."""
2183
2340
        if target_branch is None:
2184
2341
            # this format does not implement branch itself, thus the implicit
2212
2369
        return clone
2213
2370
 
2214
2371
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2215
 
             possible_transports=None, ignore_fallbacks=False):
 
2372
             possible_transports=None, ignore_fallbacks=False,
 
2373
             found_repository=None):
2216
2374
        """Return the branch that the branch reference in a_bzrdir points at.
2217
2375
 
2218
2376
        :param a_bzrdir: A BzrDir that contains a branch.
2232
2390
                raise AssertionError("wrong format %r found for %r" %
2233
2391
                    (format, self))
2234
2392
        if location is None:
2235
 
            location = self.get_reference(a_bzrdir)
 
2393
            location = self.get_reference(a_bzrdir, name)
2236
2394
        real_bzrdir = bzrdir.BzrDir.open(
2237
2395
            location, possible_transports=possible_transports)
2238
2396
        result = real_bzrdir.open_branch(name=name, 
2270
2428
BranchFormat.register_format(__format7)
2271
2429
BranchFormat.register_format(__format8)
2272
2430
BranchFormat.set_default_format(__format7)
2273
 
_legacy_formats = [BzrBranchFormat4(),
2274
 
    ]
2275
 
network_format_registry.register(
2276
 
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2277
 
 
2278
 
 
2279
 
class BranchWriteLockResult(object):
 
2431
BranchFormat.register_extra_format(BzrBranchFormat4())
 
2432
 
 
2433
 
 
2434
class BranchWriteLockResult(LogicalLockResult):
2280
2435
    """The result of write locking a branch.
2281
2436
 
2282
2437
    :ivar branch_token: The token obtained from the underlying branch lock, or
2285
2440
    """
2286
2441
 
2287
2442
    def __init__(self, unlock, branch_token):
 
2443
        LogicalLockResult.__init__(self, unlock)
2288
2444
        self.branch_token = branch_token
2289
 
        self.unlock = unlock
2290
2445
 
2291
 
    def __str__(self):
 
2446
    def __repr__(self):
2292
2447
        return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
2293
2448
            self.unlock)
2294
2449
 
2379
2534
    def lock_read(self):
2380
2535
        """Lock the branch for read operations.
2381
2536
 
2382
 
        :return: An object with an unlock method which will release the lock
2383
 
            obtained.
 
2537
        :return: A bzrlib.lock.LogicalLockResult.
2384
2538
        """
2385
2539
        if not self.is_locked():
2386
2540
            self._note_lock('r')
2394
2548
            took_lock = False
2395
2549
        try:
2396
2550
            self.control_files.lock_read()
2397
 
            return self
 
2551
            return LogicalLockResult(self.unlock)
2398
2552
        except:
2399
2553
            if took_lock:
2400
2554
                self.repository.unlock()
2556
2710
        result.target_branch = target
2557
2711
        result.old_revno, result.old_revid = target.last_revision_info()
2558
2712
        self.update_references(target)
2559
 
        if result.old_revid != self.last_revision():
 
2713
        if result.old_revid != stop_revision:
2560
2714
            # We assume that during 'push' this repository is closer than
2561
2715
            # the target.
2562
2716
            graph = self.repository.get_graph(target.repository)
2585
2739
                mode=self.bzrdir._get_file_mode())
2586
2740
 
2587
2741
 
 
2742
class BzrBranchPreSplitOut(BzrBranch):
 
2743
 
 
2744
    def _get_checkout_format(self):
 
2745
        """Return the most suitable metadir for a checkout of this branch.
 
2746
        Weaves are used if this branch's repository uses weaves.
 
2747
        """
 
2748
        from bzrlib.repofmt.weaverepo import RepositoryFormat7
 
2749
        from bzrlib.bzrdir import BzrDirMetaFormat1
 
2750
        format = BzrDirMetaFormat1()
 
2751
        format.repository_format = RepositoryFormat7()
 
2752
        return format
 
2753
 
 
2754
 
2588
2755
class BzrBranch5(BzrBranch):
2589
2756
    """A format 5 branch. This supports new features over plain branches.
2590
2757
 
3024
3191
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
3025
3192
    """
3026
3193
 
 
3194
    @deprecated_method(deprecated_in((2, 3, 0)))
3027
3195
    def __int__(self):
3028
 
        # DEPRECATED: pull used to return the change in revno
 
3196
        """Return the relative change in revno.
 
3197
 
 
3198
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3199
        """
3029
3200
        return self.new_revno - self.old_revno
3030
3201
 
3031
3202
    def report(self, to_file):
3056
3227
        target, otherwise it will be None.
3057
3228
    """
3058
3229
 
 
3230
    @deprecated_method(deprecated_in((2, 3, 0)))
3059
3231
    def __int__(self):
3060
 
        # DEPRECATED: push used to return the change in revno
 
3232
        """Return the relative change in revno.
 
3233
 
 
3234
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3235
        """
3061
3236
        return self.new_revno - self.old_revno
3062
3237
 
3063
3238
    def report(self, to_file):
3186
3361
    _optimisers = []
3187
3362
    """The available optimised InterBranch types."""
3188
3363
 
3189
 
    @staticmethod
3190
 
    def _get_branch_formats_to_test():
3191
 
        """Return a tuple with the Branch formats to use when testing."""
3192
 
        raise NotImplementedError(InterBranch._get_branch_formats_to_test)
 
3364
    @classmethod
 
3365
    def _get_branch_formats_to_test(klass):
 
3366
        """Return an iterable of format tuples for testing.
 
3367
        
 
3368
        :return: An iterable of (from_format, to_format) to use when testing
 
3369
            this InterBranch class. Each InterBranch class should define this
 
3370
            method itself.
 
3371
        """
 
3372
        raise NotImplementedError(klass._get_branch_formats_to_test)
3193
3373
 
 
3374
    @needs_write_lock
3194
3375
    def pull(self, overwrite=False, stop_revision=None,
3195
3376
             possible_transports=None, local=False):
3196
3377
        """Mirror source into target branch.
3201
3382
        """
3202
3383
        raise NotImplementedError(self.pull)
3203
3384
 
 
3385
    @needs_write_lock
3204
3386
    def update_revisions(self, stop_revision=None, overwrite=False,
3205
 
                         graph=None):
 
3387
                         graph=None, fetch_tags=True):
3206
3388
        """Pull in new perfect-fit revisions.
3207
3389
 
3208
3390
        :param stop_revision: Updated until the given revision
3210
3392
            to see if it is a proper descendant.
3211
3393
        :param graph: A Graph object that can be used to query history
3212
3394
            information. This can be None.
 
3395
        :param fetch_tags: Flag that specifies if tags from source should be
 
3396
            fetched too.
3213
3397
        :return: None
3214
3398
        """
3215
3399
        raise NotImplementedError(self.update_revisions)
3216
3400
 
 
3401
    @needs_write_lock
3217
3402
    def push(self, overwrite=False, stop_revision=None,
3218
3403
             _override_hook_source_branch=None):
3219
3404
        """Mirror the source branch into the target branch.
3222
3407
        """
3223
3408
        raise NotImplementedError(self.push)
3224
3409
 
 
3410
    @needs_write_lock
 
3411
    def copy_content_into(self, revision_id=None):
 
3412
        """Copy the content of source into target
 
3413
 
 
3414
        revision_id: if not None, the revision history in the new branch will
 
3415
                     be truncated to end with revision_id.
 
3416
        """
 
3417
        raise NotImplementedError(self.copy_content_into)
 
3418
 
3225
3419
 
3226
3420
class GenericInterBranch(InterBranch):
3227
 
    """InterBranch implementation that uses public Branch functions.
3228
 
    """
3229
 
 
3230
 
    @staticmethod
3231
 
    def _get_branch_formats_to_test():
3232
 
        return BranchFormat._default_format, BranchFormat._default_format
3233
 
 
 
3421
    """InterBranch implementation that uses public Branch functions."""
 
3422
 
 
3423
    @classmethod
 
3424
    def is_compatible(klass, source, target):
 
3425
        # GenericBranch uses the public API, so always compatible
 
3426
        return True
 
3427
 
 
3428
    @classmethod
 
3429
    def _get_branch_formats_to_test(klass):
 
3430
        return [(BranchFormat._default_format, BranchFormat._default_format)]
 
3431
 
 
3432
    @classmethod
 
3433
    def unwrap_format(klass, format):
 
3434
        if isinstance(format, remote.RemoteBranchFormat):
 
3435
            format._ensure_real()
 
3436
            return format._custom_format
 
3437
        return format
 
3438
 
 
3439
    @needs_write_lock
 
3440
    def copy_content_into(self, revision_id=None):
 
3441
        """Copy the content of source into target
 
3442
 
 
3443
        revision_id: if not None, the revision history in the new branch will
 
3444
                     be truncated to end with revision_id.
 
3445
        """
 
3446
        self.source.update_references(self.target)
 
3447
        self.source._synchronize_history(self.target, revision_id)
 
3448
        try:
 
3449
            parent = self.source.get_parent()
 
3450
        except errors.InaccessibleParent, e:
 
3451
            mutter('parent was not accessible to copy: %s', e)
 
3452
        else:
 
3453
            if parent:
 
3454
                self.target.set_parent(parent)
 
3455
        if self.source._push_should_merge_tags():
 
3456
            self.source.tags.merge_to(self.target.tags)
 
3457
 
 
3458
    @needs_write_lock
3234
3459
    def update_revisions(self, stop_revision=None, overwrite=False,
3235
 
        graph=None):
 
3460
        graph=None, fetch_tags=True):
3236
3461
        """See InterBranch.update_revisions()."""
3237
 
        self.source.lock_read()
3238
 
        try:
3239
 
            other_revno, other_last_revision = self.source.last_revision_info()
3240
 
            stop_revno = None # unknown
3241
 
            if stop_revision is None:
3242
 
                stop_revision = other_last_revision
3243
 
                if _mod_revision.is_null(stop_revision):
3244
 
                    # if there are no commits, we're done.
3245
 
                    return
3246
 
                stop_revno = other_revno
3247
 
 
3248
 
            # what's the current last revision, before we fetch [and change it
3249
 
            # possibly]
3250
 
            last_rev = _mod_revision.ensure_null(self.target.last_revision())
3251
 
            # we fetch here so that we don't process data twice in the common
3252
 
            # case of having something to pull, and so that the check for
3253
 
            # already merged can operate on the just fetched graph, which will
3254
 
            # be cached in memory.
3255
 
            self.target.fetch(self.source, stop_revision)
3256
 
            # Check to see if one is an ancestor of the other
3257
 
            if not overwrite:
3258
 
                if graph is None:
3259
 
                    graph = self.target.repository.get_graph()
3260
 
                if self.target._check_if_descendant_or_diverged(
3261
 
                        stop_revision, last_rev, graph, self.source):
3262
 
                    # stop_revision is a descendant of last_rev, but we aren't
3263
 
                    # overwriting, so we're done.
3264
 
                    return
3265
 
            if stop_revno is None:
3266
 
                if graph is None:
3267
 
                    graph = self.target.repository.get_graph()
3268
 
                this_revno, this_last_revision = \
3269
 
                        self.target.last_revision_info()
3270
 
                stop_revno = graph.find_distance_to_null(stop_revision,
3271
 
                                [(other_last_revision, other_revno),
3272
 
                                 (this_last_revision, this_revno)])
3273
 
            self.target.set_last_revision_info(stop_revno, stop_revision)
3274
 
        finally:
3275
 
            self.source.unlock()
3276
 
 
 
3462
        other_revno, other_last_revision = self.source.last_revision_info()
 
3463
        stop_revno = None # unknown
 
3464
        if stop_revision is None:
 
3465
            stop_revision = other_last_revision
 
3466
            if _mod_revision.is_null(stop_revision):
 
3467
                # if there are no commits, we're done.
 
3468
                return
 
3469
            stop_revno = other_revno
 
3470
 
 
3471
        # what's the current last revision, before we fetch [and change it
 
3472
        # possibly]
 
3473
        last_rev = _mod_revision.ensure_null(self.target.last_revision())
 
3474
        # we fetch here so that we don't process data twice in the common
 
3475
        # case of having something to pull, and so that the check for
 
3476
        # already merged can operate on the just fetched graph, which will
 
3477
        # be cached in memory.
 
3478
        if fetch_tags:
 
3479
            fetch_spec_factory = fetch.FetchSpecFactory()
 
3480
            fetch_spec_factory.source_branch = self.source
 
3481
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
 
3482
            fetch_spec_factory.source_repo = self.source.repository
 
3483
            fetch_spec_factory.target_repo = self.target.repository
 
3484
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
 
3485
            fetch_spec = fetch_spec_factory.make_fetch_spec()
 
3486
        else:
 
3487
            fetch_spec = _mod_graph.NotInOtherForRevs(self.target.repository,
 
3488
                self.source.repository, revision_ids=[stop_revision]).execute()
 
3489
        self.target.fetch(self.source, fetch_spec=fetch_spec)
 
3490
        # Check to see if one is an ancestor of the other
 
3491
        if not overwrite:
 
3492
            if graph is None:
 
3493
                graph = self.target.repository.get_graph()
 
3494
            if self.target._check_if_descendant_or_diverged(
 
3495
                    stop_revision, last_rev, graph, self.source):
 
3496
                # stop_revision is a descendant of last_rev, but we aren't
 
3497
                # overwriting, so we're done.
 
3498
                return
 
3499
        if stop_revno is None:
 
3500
            if graph is None:
 
3501
                graph = self.target.repository.get_graph()
 
3502
            this_revno, this_last_revision = \
 
3503
                    self.target.last_revision_info()
 
3504
            stop_revno = graph.find_distance_to_null(stop_revision,
 
3505
                            [(other_last_revision, other_revno),
 
3506
                             (this_last_revision, this_revno)])
 
3507
        self.target.set_last_revision_info(stop_revno, stop_revision)
 
3508
 
 
3509
    @needs_write_lock
3277
3510
    def pull(self, overwrite=False, stop_revision=None,
3278
 
             possible_transports=None, _hook_master=None, run_hooks=True,
 
3511
             possible_transports=None, run_hooks=True,
3279
3512
             _override_hook_target=None, local=False):
3280
 
        """See Branch.pull.
 
3513
        """Pull from source into self, updating my master if any.
3281
3514
 
3282
 
        :param _hook_master: Private parameter - set the branch to
3283
 
            be supplied as the master to pull hooks.
3284
3515
        :param run_hooks: Private parameter - if false, this branch
3285
3516
            is being called because it's the master of the primary branch,
3286
3517
            so it should not run its hooks.
3287
 
        :param _override_hook_target: Private parameter - set the branch to be
3288
 
            supplied as the target_branch to pull hooks.
3289
 
        :param local: Only update the local branch, and not the bound branch.
3290
3518
        """
3291
 
        # This type of branch can't be bound.
3292
 
        if local:
 
3519
        bound_location = self.target.get_bound_location()
 
3520
        if local and not bound_location:
3293
3521
            raise errors.LocalRequiresBoundBranch()
3294
 
        result = PullResult()
3295
 
        result.source_branch = self.source
3296
 
        if _override_hook_target is None:
3297
 
            result.target_branch = self.target
3298
 
        else:
3299
 
            result.target_branch = _override_hook_target
3300
 
        self.source.lock_read()
 
3522
        master_branch = None
 
3523
        source_is_master = (self.source.user_url == bound_location)
 
3524
        if not local and bound_location and not source_is_master:
 
3525
            # not pulling from master, so we need to update master.
 
3526
            master_branch = self.target.get_master_branch(possible_transports)
 
3527
            master_branch.lock_write()
3301
3528
        try:
3302
 
            # We assume that during 'pull' the target repository is closer than
3303
 
            # the source one.
3304
 
            self.source.update_references(self.target)
3305
 
            graph = self.target.repository.get_graph(self.source.repository)
3306
 
            # TODO: Branch formats should have a flag that indicates 
3307
 
            # that revno's are expensive, and pull() should honor that flag.
3308
 
            # -- JRV20090506
3309
 
            result.old_revno, result.old_revid = \
3310
 
                self.target.last_revision_info()
3311
 
            self.target.update_revisions(self.source, stop_revision,
3312
 
                overwrite=overwrite, graph=graph)
3313
 
            # TODO: The old revid should be specified when merging tags, 
3314
 
            # so a tags implementation that versions tags can only 
3315
 
            # pull in the most recent changes. -- JRV20090506
3316
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3317
 
                overwrite)
3318
 
            result.new_revno, result.new_revid = self.target.last_revision_info()
3319
 
            if _hook_master:
3320
 
                result.master_branch = _hook_master
3321
 
                result.local_branch = result.target_branch
3322
 
            else:
3323
 
                result.master_branch = result.target_branch
3324
 
                result.local_branch = None
3325
 
            if run_hooks:
3326
 
                for hook in Branch.hooks['post_pull']:
3327
 
                    hook(result)
 
3529
            if master_branch:
 
3530
                # pull from source into master.
 
3531
                master_branch.pull(self.source, overwrite, stop_revision,
 
3532
                    run_hooks=False)
 
3533
            return self._pull(overwrite,
 
3534
                stop_revision, _hook_master=master_branch,
 
3535
                run_hooks=run_hooks,
 
3536
                _override_hook_target=_override_hook_target,
 
3537
                merge_tags_to_master=not source_is_master)
3328
3538
        finally:
3329
 
            self.source.unlock()
3330
 
        return result
 
3539
            if master_branch:
 
3540
                master_branch.unlock()
3331
3541
 
3332
3542
    def push(self, overwrite=False, stop_revision=None,
3333
3543
             _override_hook_source_branch=None):
3373
3583
                # push into the master from the source branch.
3374
3584
                self.source._basic_push(master_branch, overwrite, stop_revision)
3375
3585
                # and push into the target branch from the source. Note that we
3376
 
                # push from the source branch again, because its considered the
 
3586
                # push from the source branch again, because it's considered the
3377
3587
                # highest bandwidth repository.
3378
3588
                result = self.source._basic_push(self.target, overwrite,
3379
3589
                    stop_revision)
3395
3605
            _run_hooks()
3396
3606
            return result
3397
3607
 
3398
 
    @classmethod
3399
 
    def is_compatible(self, source, target):
3400
 
        # GenericBranch uses the public API, so always compatible
3401
 
        return True
3402
 
 
3403
 
 
3404
 
class InterToBranch5(GenericInterBranch):
3405
 
 
3406
 
    @staticmethod
3407
 
    def _get_branch_formats_to_test():
3408
 
        return BranchFormat._default_format, BzrBranchFormat5()
3409
 
 
3410
 
    def pull(self, overwrite=False, stop_revision=None,
3411
 
             possible_transports=None, run_hooks=True,
3412
 
             _override_hook_target=None, local=False):
3413
 
        """Pull from source into self, updating my master if any.
3414
 
 
 
3608
    def _pull(self, overwrite=False, stop_revision=None,
 
3609
             possible_transports=None, _hook_master=None, run_hooks=True,
 
3610
             _override_hook_target=None, local=False,
 
3611
             merge_tags_to_master=True):
 
3612
        """See Branch.pull.
 
3613
 
 
3614
        This function is the core worker, used by GenericInterBranch.pull to
 
3615
        avoid duplication when pulling source->master and source->local.
 
3616
 
 
3617
        :param _hook_master: Private parameter - set the branch to
 
3618
            be supplied as the master to pull hooks.
3415
3619
        :param run_hooks: Private parameter - if false, this branch
3416
3620
            is being called because it's the master of the primary branch,
3417
3621
            so it should not run its hooks.
 
3622
        :param _override_hook_target: Private parameter - set the branch to be
 
3623
            supplied as the target_branch to pull hooks.
 
3624
        :param local: Only update the local branch, and not the bound branch.
3418
3625
        """
3419
 
        bound_location = self.target.get_bound_location()
3420
 
        if local and not bound_location:
 
3626
        # This type of branch can't be bound.
 
3627
        if local:
3421
3628
            raise errors.LocalRequiresBoundBranch()
3422
 
        master_branch = None
3423
 
        if not local and bound_location and self.source.user_url != bound_location:
3424
 
            # not pulling from master, so we need to update master.
3425
 
            master_branch = self.target.get_master_branch(possible_transports)
3426
 
            master_branch.lock_write()
 
3629
        result = PullResult()
 
3630
        result.source_branch = self.source
 
3631
        if _override_hook_target is None:
 
3632
            result.target_branch = self.target
 
3633
        else:
 
3634
            result.target_branch = _override_hook_target
 
3635
        self.source.lock_read()
3427
3636
        try:
3428
 
            if master_branch:
3429
 
                # pull from source into master.
3430
 
                master_branch.pull(self.source, overwrite, stop_revision,
3431
 
                    run_hooks=False)
3432
 
            return super(InterToBranch5, self).pull(overwrite,
3433
 
                stop_revision, _hook_master=master_branch,
3434
 
                run_hooks=run_hooks,
3435
 
                _override_hook_target=_override_hook_target)
 
3637
            # We assume that during 'pull' the target repository is closer than
 
3638
            # the source one.
 
3639
            self.source.update_references(self.target)
 
3640
            graph = self.target.repository.get_graph(self.source.repository)
 
3641
            # TODO: Branch formats should have a flag that indicates 
 
3642
            # that revno's are expensive, and pull() should honor that flag.
 
3643
            # -- JRV20090506
 
3644
            result.old_revno, result.old_revid = \
 
3645
                self.target.last_revision_info()
 
3646
            self.target.update_revisions(self.source, stop_revision,
 
3647
                overwrite=overwrite, graph=graph)
 
3648
            # TODO: The old revid should be specified when merging tags, 
 
3649
            # so a tags implementation that versions tags can only 
 
3650
            # pull in the most recent changes. -- JRV20090506
 
3651
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3652
                overwrite, ignore_master=not merge_tags_to_master)
 
3653
            result.new_revno, result.new_revid = self.target.last_revision_info()
 
3654
            if _hook_master:
 
3655
                result.master_branch = _hook_master
 
3656
                result.local_branch = result.target_branch
 
3657
            else:
 
3658
                result.master_branch = result.target_branch
 
3659
                result.local_branch = None
 
3660
            if run_hooks:
 
3661
                for hook in Branch.hooks['post_pull']:
 
3662
                    hook(result)
3436
3663
        finally:
3437
 
            if master_branch:
3438
 
                master_branch.unlock()
 
3664
            self.source.unlock()
 
3665
        return result
3439
3666
 
3440
3667
 
3441
3668
InterBranch.register_optimiser(GenericInterBranch)
3442
 
InterBranch.register_optimiser(InterToBranch5)