/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

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, pb=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
635
        :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.
676
636
        :return: None
677
637
        """
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.")
681
638
        if self.base == from_branch.base:
682
639
            return (0, [])
683
640
        if pb is not None:
686
643
                % "pb parameter to fetch()")
687
644
        from_branch.lock_read()
688
645
        try:
689
 
            if last_revision is None and fetch_spec is None:
 
646
            if last_revision is None:
690
647
                last_revision = from_branch.last_revision()
691
648
                last_revision = _mod_revision.ensure_null(last_revision)
692
649
            return self.repository.fetch(from_branch.repository,
693
650
                                         revision_id=last_revision,
694
 
                                         pb=pb, fetch_spec=fetch_spec)
 
651
                                         pb=pb)
695
652
        finally:
696
653
            from_branch.unlock()
697
654
 
825
782
            old_repository = self.repository
826
783
            if len(old_repository._fallback_repositories) != 1:
827
784
                raise AssertionError("can't cope with fallback repositories "
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()
 
785
                    "of %r" % (self.repository,))
 
786
            # unlock it, including unlocking the fallback
857
787
            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):
 
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
876
807
                self.repository.lock_write()
877
 
            # Fetch from the old repository into the new.
878
 
            old_repository.lock_read()
879
 
            try:
880
808
                # XXX: If you unstack a branch while it has a working tree
881
809
                # with a pending merge, the pending-merged revisions will no
882
810
                # longer be present.  You can (probably) revert and remerge.
897
825
 
898
826
        :seealso: Branch._get_tags_bytes.
899
827
        """
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)
 
828
        return _run_with_write_locked_target(self, self._transport.put_bytes,
 
829
            'tags', bytes)
906
830
 
907
831
    def _cache_revision_history(self, rev_history):
908
832
        """Set the cached revision history to rev_history.
938
862
        self._merge_sorted_revisions_cache = None
939
863
        self._partial_revision_history_cache = []
940
864
        self._partial_revision_id_to_revno_cache = {}
941
 
        self._tags_bytes = None
942
865
 
943
866
    def _gen_revision_history(self):
944
867
        """Return sequence of revision hashes on to this branch.
1028
951
                raise errors.NoSuchRevision(self, stop_revision)
1029
952
        return other_history[self_len:stop_revision]
1030
953
 
 
954
    @needs_write_lock
1031
955
    def update_revisions(self, other, stop_revision=None, overwrite=False,
1032
 
                         graph=None, fetch_tags=True):
 
956
                         graph=None):
1033
957
        """Pull in new perfect-fit revisions.
1034
958
 
1035
959
        :param other: Another Branch to pull from
1038
962
            to see if it is a proper descendant.
1039
963
        :param graph: A Graph object that can be used to query history
1040
964
            information. This can be None.
1041
 
        :param fetch_tags: Flag that specifies if tags from other should be
1042
 
            fetched too.
1043
965
        :return: None
1044
966
        """
1045
967
        return InterBranch.get(other, self).update_revisions(stop_revision,
1046
 
            overwrite, graph, fetch_tags=fetch_tags)
 
968
            overwrite, graph)
1047
969
 
1048
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1049
970
    def import_last_revision_info(self, source_repo, revno, revid):
1050
971
        """Set the last revision info, importing from another repo if necessary.
1051
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
 
1052
976
        :param source_repo: Source repository to optionally fetch from
1053
977
        :param revno: Revision number of the new tip
1054
978
        :param revid: Revision id of the new tip
1057
981
            self.repository.fetch(source_repo, revision_id=revid)
1058
982
        self.set_last_revision_info(revno, revid)
1059
983
 
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
 
 
1082
984
    def revision_id_to_revno(self, revision_id):
1083
985
        """Given a revision id, return its revno"""
1084
986
        if _mod_revision.is_null(revision_id):
1104
1006
            self._extend_partial_history(distance_from_last)
1105
1007
        return self._partial_revision_history_cache[distance_from_last]
1106
1008
 
 
1009
    @needs_write_lock
1107
1010
    def pull(self, source, overwrite=False, stop_revision=None,
1108
1011
             possible_transports=None, *args, **kwargs):
1109
1012
        """Mirror source into this branch.
1305
1208
        return result
1306
1209
 
1307
1210
    @needs_read_lock
1308
 
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
1309
 
            repository=None):
 
1211
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
1310
1212
        """Create a new line of development from the branch, into to_bzrdir.
1311
1213
 
1312
1214
        to_bzrdir controls the branch format.
1317
1219
        if (repository_policy is not None and
1318
1220
            repository_policy.requires_stacking()):
1319
1221
            to_bzrdir._format.require_stacking(_skip_repo=True)
1320
 
        result = to_bzrdir.create_branch(repository=repository)
 
1222
        result = to_bzrdir.create_branch()
1321
1223
        result.lock_write()
1322
1224
        try:
1323
1225
            if repository_policy is not None:
1353
1255
                revno = 1
1354
1256
        destination.set_last_revision_info(revno, revision_id)
1355
1257
 
 
1258
    @needs_read_lock
1356
1259
    def copy_content_into(self, destination, revision_id=None):
1357
1260
        """Copy the content of self into destination.
1358
1261
 
1359
1262
        revision_id: if not None, the revision history in the new branch will
1360
1263
                     be truncated to end with revision_id.
1361
1264
        """
1362
 
        return InterBranch.get(self, destination).copy_content_into(
1363
 
            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)
1364
1276
 
1365
1277
    def update_references(self, target):
1366
1278
        if not getattr(self._format, 'supports_reference_locations', False):
1411
1323
        """Return the most suitable metadir for a checkout of this branch.
1412
1324
        Weaves are used if this branch's repository uses weaves.
1413
1325
        """
1414
 
        format = self.repository.bzrdir.checkout_metadir()
1415
 
        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)
1416
1333
        return format
1417
1334
 
1418
1335
    def create_clone_on_transport(self, to_transport, revision_id=None,
1419
 
        stacked_on=None, create_prefix=False, use_existing_dir=False,
1420
 
        no_tree=None):
 
1336
        stacked_on=None, create_prefix=False, use_existing_dir=False):
1421
1337
        """Create a clone of this branch and its bzrdir.
1422
1338
 
1423
1339
        :param to_transport: The transport to clone onto.
1430
1346
        """
1431
1347
        # XXX: Fix the bzrdir API to allow getting the branch back from the
1432
1348
        # 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
1435
1349
        if revision_id is None:
1436
1350
            revision_id = self.last_revision()
1437
1351
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1438
1352
            revision_id=revision_id, stacked_on=stacked_on,
1439
 
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
1440
 
            no_tree=no_tree)
 
1353
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1441
1354
        return dir_to.open_branch()
1442
1355
 
1443
1356
    def create_checkout(self, to_location, revision_id=None,
1568
1481
     * an open routine.
1569
1482
 
1570
1483
    Formats are placed in an dict by their format string for reference
1571
 
    during branch opening. It's not required that these be instances, they
 
1484
    during branch opening. Its not required that these be instances, they
1572
1485
    can be classes themselves with class methods - it simply depends on
1573
1486
    whether state is needed for a given format or not.
1574
1487
 
1583
1496
    _formats = {}
1584
1497
    """The known formats."""
1585
1498
 
1586
 
    _extra_formats = []
1587
 
    """Extra formats that can not be part of a metadir."""
1588
 
 
1589
1499
    can_set_append_revisions_only = True
1590
1500
 
1591
1501
    def __eq__(self, other):
1600
1510
        try:
1601
1511
            transport = a_bzrdir.get_branch_transport(None, name=name)
1602
1512
            format_string = transport.get_bytes("format")
1603
 
            format = klass._formats[format_string]
1604
 
            if isinstance(format, MetaDirBranchFormatFactory):
1605
 
                return format()
1606
 
            return format
 
1513
            return klass._formats[format_string]
1607
1514
        except errors.NoSuchFile:
1608
1515
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1609
1516
        except KeyError:
1614
1521
        """Return the current default format."""
1615
1522
        return klass._default_format
1616
1523
 
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):
 
1524
    def get_reference(self, a_bzrdir):
1632
1525
        """Get the target reference of the branch in a_bzrdir.
1633
1526
 
1634
1527
        format probing must have been completed before calling
1636
1529
        in a_bzrdir is correct.
1637
1530
 
1638
1531
        :param a_bzrdir: The bzrdir to get the branch data from.
1639
 
        :param name: Name of the colocated branch to fetch
1640
1532
        :return: None if the branch is not a reference branch.
1641
1533
        """
1642
1534
        return None
1643
1535
 
1644
1536
    @classmethod
1645
 
    def set_reference(self, a_bzrdir, name, to_branch):
 
1537
    def set_reference(self, a_bzrdir, to_branch):
1646
1538
        """Set the target reference of the branch in a_bzrdir.
1647
1539
 
1648
1540
        format probing must have been completed before calling
1650
1542
        in a_bzrdir is correct.
1651
1543
 
1652
1544
        :param a_bzrdir: The bzrdir to set the branch reference for.
1653
 
        :param name: Name of colocated branch to set, None for default
1654
1545
        :param to_branch: branch that the checkout is to reference
1655
1546
        """
1656
1547
        raise NotImplementedError(self.set_reference)
1672
1563
            hook(params)
1673
1564
 
1674
1565
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1675
 
                           repository=None, lock_type='metadir',
1676
 
                           set_format=True):
 
1566
                           lock_type='metadir', set_format=True):
1677
1567
        """Initialize a branch in a bzrdir, with specified files
1678
1568
 
1679
1569
        :param a_bzrdir: The bzrdir to initialize the branch in
1713
1603
        finally:
1714
1604
            if lock_taken:
1715
1605
                control_files.unlock()
1716
 
        branch = self.open(a_bzrdir, name, _found=True,
1717
 
                found_repository=repository)
 
1606
        branch = self.open(a_bzrdir, name, _found=True)
1718
1607
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1719
1608
        return branch
1720
1609
 
1721
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
1610
    def initialize(self, a_bzrdir, name=None):
1722
1611
        """Create a branch of this format in a_bzrdir.
1723
1612
        
1724
1613
        :param name: Name of the colocated branch to create.
1758
1647
        """
1759
1648
        raise NotImplementedError(self.network_name)
1760
1649
 
1761
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
1762
 
            found_repository=None):
 
1650
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1763
1651
        """Return the branch object for a_bzrdir
1764
1652
 
1765
1653
        :param a_bzrdir: A BzrDir that contains a branch.
1772
1660
        raise NotImplementedError(self.open)
1773
1661
 
1774
1662
    @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
1786
1663
    def register_format(klass, 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
 
        """
 
1664
        """Register a metadir format."""
1792
1665
        klass._formats[format.get_format_string()] = format
1793
1666
        # Metadir formats have a network name of their format string, and get
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__)
 
1667
        # registered as class factories.
 
1668
        network_format_registry.register(format.get_format_string(), format.__class__)
1800
1669
 
1801
1670
    @classmethod
1802
1671
    def set_default_format(klass, format):
1814
1683
    def unregister_format(klass, format):
1815
1684
        del klass._formats[format.get_format_string()]
1816
1685
 
1817
 
    @classmethod
1818
 
    def unregister_extra_format(klass, format):
1819
 
        klass._extra_formats.remove(format)
1820
 
 
1821
1686
    def __str__(self):
1822
1687
        return self.get_format_description().rstrip()
1823
1688
 
1826
1691
        return False  # by default
1827
1692
 
1828
1693
 
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
 
 
1857
1694
class BranchHooks(Hooks):
1858
1695
    """A dictionary mapping hook name to a list of callables for branch hooks.
1859
1696
 
1886
1723
            "with a bzrlib.branch.PullResult object and only runs in the "
1887
1724
            "bzr client.", (0, 15), None))
1888
1725
        self.create_hook(HookPoint('pre_commit',
1889
 
            "Called after a commit is calculated but before it is "
 
1726
            "Called after a commit is calculated but before it is is "
1890
1727
            "completed. pre_commit is called with (local, master, old_revno, "
1891
1728
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1892
1729
            "). old_revid is NULL_REVISION for the first commit to a branch, "
1929
1766
            "all are called with the url returned from the previous hook."
1930
1767
            "The order is however undefined.", (1, 9), None))
1931
1768
        self.create_hook(HookPoint('automatic_tag_name',
1932
 
            "Called to determine an automatic tag name for a revision. "
 
1769
            "Called to determine an automatic tag name for a revision."
1933
1770
            "automatic_tag_name is called with (branch, revision_id) and "
1934
1771
            "should return a tag name or None if no tag name could be "
1935
1772
            "determined. The first non-None tag name returned will be used.",
2026
1863
        return self.__dict__ == other.__dict__
2027
1864
 
2028
1865
    def __repr__(self):
2029
 
        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)
2030
1872
 
2031
1873
 
2032
1874
class SwitchHookParams(object):
2074
1916
        """See BranchFormat.get_format_description()."""
2075
1917
        return "Branch format 4"
2076
1918
 
2077
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
1919
    def initialize(self, a_bzrdir, name=None):
2078
1920
        """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,))
2082
1921
        utf8_files = [('revision-history', ''),
2083
1922
                      ('branch-name', ''),
2084
1923
                      ]
2093
1932
        """The network name for this format is the control dirs disk label."""
2094
1933
        return self._matchingbzrdir.get_format_string()
2095
1934
 
2096
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2097
 
            found_repository=None):
 
1935
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2098
1936
        """See BranchFormat.open()."""
2099
1937
        if not _found:
2100
1938
            # we are being called directly and must probe.
2101
1939
            raise NotImplementedError
2102
 
        if found_repository is None:
2103
 
            found_repository = a_bzrdir.open_repository()
2104
 
        return BzrBranchPreSplitOut(_format=self,
 
1940
        return BzrBranch(_format=self,
2105
1941
                         _control_files=a_bzrdir._control_files,
2106
1942
                         a_bzrdir=a_bzrdir,
2107
1943
                         name=name,
2108
 
                         _repository=found_repository)
 
1944
                         _repository=a_bzrdir.open_repository())
2109
1945
 
2110
1946
    def __str__(self):
2111
1947
        return "Bazaar-NG branch format 4"
2125
1961
        """
2126
1962
        return self.get_format_string()
2127
1963
 
2128
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2129
 
            found_repository=None):
 
1964
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2130
1965
        """See BranchFormat.open()."""
2131
1966
        if not _found:
2132
1967
            format = BranchFormat.find_format(a_bzrdir, name=name)
2137
1972
        try:
2138
1973
            control_files = lockable_files.LockableFiles(transport, 'lock',
2139
1974
                                                         lockdir.LockDir)
2140
 
            if found_repository is None:
2141
 
                found_repository = a_bzrdir.find_repository()
2142
1975
            return self._branch_class()(_format=self,
2143
1976
                              _control_files=control_files,
2144
1977
                              name=name,
2145
1978
                              a_bzrdir=a_bzrdir,
2146
 
                              _repository=found_repository,
 
1979
                              _repository=a_bzrdir.find_repository(),
2147
1980
                              ignore_fallbacks=ignore_fallbacks)
2148
1981
        except errors.NoSuchFile:
2149
1982
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2181
2014
        """See BranchFormat.get_format_description()."""
2182
2015
        return "Branch format 5"
2183
2016
 
2184
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2017
    def initialize(self, a_bzrdir, name=None):
2185
2018
        """Create a branch of this format in a_bzrdir."""
2186
2019
        utf8_files = [('revision-history', ''),
2187
2020
                      ('branch-name', ''),
2188
2021
                      ]
2189
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2022
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2190
2023
 
2191
2024
    def supports_tags(self):
2192
2025
        return False
2214
2047
        """See BranchFormat.get_format_description()."""
2215
2048
        return "Branch format 6"
2216
2049
 
2217
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2050
    def initialize(self, a_bzrdir, name=None):
2218
2051
        """Create a branch of this format in a_bzrdir."""
2219
2052
        utf8_files = [('last-revision', '0 null:\n'),
2220
2053
                      ('branch.conf', ''),
2221
2054
                      ('tags', ''),
2222
2055
                      ]
2223
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2056
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2224
2057
 
2225
2058
    def make_tags(self, branch):
2226
2059
        """See bzrlib.branch.BranchFormat.make_tags()."""
2244
2077
        """See BranchFormat.get_format_description()."""
2245
2078
        return "Branch format 8"
2246
2079
 
2247
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2080
    def initialize(self, a_bzrdir, name=None):
2248
2081
        """Create a branch of this format in a_bzrdir."""
2249
2082
        utf8_files = [('last-revision', '0 null:\n'),
2250
2083
                      ('branch.conf', ''),
2251
2084
                      ('tags', ''),
2252
2085
                      ('references', '')
2253
2086
                      ]
2254
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2087
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2255
2088
 
2256
2089
    def __init__(self):
2257
2090
        super(BzrBranchFormat8, self).__init__()
2280
2113
    This format was introduced in bzr 1.6.
2281
2114
    """
2282
2115
 
2283
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2116
    def initialize(self, a_bzrdir, name=None):
2284
2117
        """Create a branch of this format in a_bzrdir."""
2285
2118
        utf8_files = [('last-revision', '0 null:\n'),
2286
2119
                      ('branch.conf', ''),
2287
2120
                      ('tags', ''),
2288
2121
                      ]
2289
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2122
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2290
2123
 
2291
2124
    def _branch_class(self):
2292
2125
        return BzrBranch7
2324
2157
        """See BranchFormat.get_format_description()."""
2325
2158
        return "Checkout reference format 1"
2326
2159
 
2327
 
    def get_reference(self, a_bzrdir, name=None):
 
2160
    def get_reference(self, a_bzrdir):
2328
2161
        """See BranchFormat.get_reference()."""
2329
 
        transport = a_bzrdir.get_branch_transport(None, name=name)
 
2162
        transport = a_bzrdir.get_branch_transport(None)
2330
2163
        return transport.get_bytes('location')
2331
2164
 
2332
 
    def set_reference(self, a_bzrdir, name, to_branch):
 
2165
    def set_reference(self, a_bzrdir, to_branch):
2333
2166
        """See BranchFormat.set_reference()."""
2334
 
        transport = a_bzrdir.get_branch_transport(None, name=name)
 
2167
        transport = a_bzrdir.get_branch_transport(None)
2335
2168
        location = transport.put_bytes('location', to_branch.base)
2336
2169
 
2337
 
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2338
 
            repository=None):
 
2170
    def initialize(self, a_bzrdir, name=None, target_branch=None):
2339
2171
        """Create a branch of this format in a_bzrdir."""
2340
2172
        if target_branch is None:
2341
2173
            # this format does not implement branch itself, thus the implicit
2369
2201
        return clone
2370
2202
 
2371
2203
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2372
 
             possible_transports=None, ignore_fallbacks=False,
2373
 
             found_repository=None):
 
2204
             possible_transports=None, ignore_fallbacks=False):
2374
2205
        """Return the branch that the branch reference in a_bzrdir points at.
2375
2206
 
2376
2207
        :param a_bzrdir: A BzrDir that contains a branch.
2390
2221
                raise AssertionError("wrong format %r found for %r" %
2391
2222
                    (format, self))
2392
2223
        if location is None:
2393
 
            location = self.get_reference(a_bzrdir, name)
 
2224
            location = self.get_reference(a_bzrdir)
2394
2225
        real_bzrdir = bzrdir.BzrDir.open(
2395
2226
            location, possible_transports=possible_transports)
2396
2227
        result = real_bzrdir.open_branch(name=name, 
2428
2259
BranchFormat.register_format(__format7)
2429
2260
BranchFormat.register_format(__format8)
2430
2261
BranchFormat.set_default_format(__format7)
2431
 
BranchFormat.register_extra_format(BzrBranchFormat4())
2432
 
 
2433
 
 
2434
 
class BranchWriteLockResult(LogicalLockResult):
2435
 
    """The result of write locking a branch.
2436
 
 
2437
 
    :ivar branch_token: The token obtained from the underlying branch lock, or
2438
 
        None.
2439
 
    :ivar unlock: A callable which will unlock the lock.
2440
 
    """
2441
 
 
2442
 
    def __init__(self, unlock, branch_token):
2443
 
        LogicalLockResult.__init__(self, unlock)
2444
 
        self.branch_token = branch_token
2445
 
 
2446
 
    def __repr__(self):
2447
 
        return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
2448
 
            self.unlock)
 
2262
_legacy_formats = [BzrBranchFormat4(),
 
2263
    ]
 
2264
network_format_registry.register(
 
2265
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2449
2266
 
2450
2267
 
2451
2268
class BzrBranch(Branch, _RelockDebugMixin):
2507
2324
        return self.control_files.is_locked()
2508
2325
 
2509
2326
    def lock_write(self, token=None):
2510
 
        """Lock the branch for write operations.
2511
 
 
2512
 
        :param token: A token to permit reacquiring a previously held and
2513
 
            preserved lock.
2514
 
        :return: A BranchWriteLockResult.
2515
 
        """
2516
2327
        if not self.is_locked():
2517
2328
            self._note_lock('w')
2518
2329
        # All-in-one needs to always unlock/lock.
2524
2335
        else:
2525
2336
            took_lock = False
2526
2337
        try:
2527
 
            return BranchWriteLockResult(self.unlock,
2528
 
                self.control_files.lock_write(token=token))
 
2338
            return self.control_files.lock_write(token=token)
2529
2339
        except:
2530
2340
            if took_lock:
2531
2341
                self.repository.unlock()
2532
2342
            raise
2533
2343
 
2534
2344
    def lock_read(self):
2535
 
        """Lock the branch for read operations.
2536
 
 
2537
 
        :return: A bzrlib.lock.LogicalLockResult.
2538
 
        """
2539
2345
        if not self.is_locked():
2540
2346
            self._note_lock('r')
2541
2347
        # All-in-one needs to always unlock/lock.
2548
2354
            took_lock = False
2549
2355
        try:
2550
2356
            self.control_files.lock_read()
2551
 
            return LogicalLockResult(self.unlock)
2552
2357
        except:
2553
2358
            if took_lock:
2554
2359
                self.repository.unlock()
2710
2515
        result.target_branch = target
2711
2516
        result.old_revno, result.old_revid = target.last_revision_info()
2712
2517
        self.update_references(target)
2713
 
        if result.old_revid != stop_revision:
 
2518
        if result.old_revid != self.last_revision():
2714
2519
            # We assume that during 'push' this repository is closer than
2715
2520
            # the target.
2716
2521
            graph = self.repository.get_graph(target.repository)
2739
2544
                mode=self.bzrdir._get_file_mode())
2740
2545
 
2741
2546
 
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
 
 
2755
2547
class BzrBranch5(BzrBranch):
2756
2548
    """A format 5 branch. This supports new features over plain branches.
2757
2549
 
3191
2983
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
3192
2984
    """
3193
2985
 
3194
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3195
2986
    def __int__(self):
3196
 
        """Return the relative change in revno.
3197
 
 
3198
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3199
 
        """
 
2987
        # DEPRECATED: pull used to return the change in revno
3200
2988
        return self.new_revno - self.old_revno
3201
2989
 
3202
2990
    def report(self, to_file):
3227
3015
        target, otherwise it will be None.
3228
3016
    """
3229
3017
 
3230
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3231
3018
    def __int__(self):
3232
 
        """Return the relative change in revno.
3233
 
 
3234
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3235
 
        """
 
3019
        # DEPRECATED: push used to return the change in revno
3236
3020
        return self.new_revno - self.old_revno
3237
3021
 
3238
3022
    def report(self, to_file):
3361
3145
    _optimisers = []
3362
3146
    """The available optimised InterBranch types."""
3363
3147
 
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)
 
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)
3373
3152
 
3374
 
    @needs_write_lock
3375
3153
    def pull(self, overwrite=False, stop_revision=None,
3376
3154
             possible_transports=None, local=False):
3377
3155
        """Mirror source into target branch.
3382
3160
        """
3383
3161
        raise NotImplementedError(self.pull)
3384
3162
 
3385
 
    @needs_write_lock
3386
3163
    def update_revisions(self, stop_revision=None, overwrite=False,
3387
 
                         graph=None, fetch_tags=True):
 
3164
                         graph=None):
3388
3165
        """Pull in new perfect-fit revisions.
3389
3166
 
3390
3167
        :param stop_revision: Updated until the given revision
3392
3169
            to see if it is a proper descendant.
3393
3170
        :param graph: A Graph object that can be used to query history
3394
3171
            information. This can be None.
3395
 
        :param fetch_tags: Flag that specifies if tags from source should be
3396
 
            fetched too.
3397
3172
        :return: None
3398
3173
        """
3399
3174
        raise NotImplementedError(self.update_revisions)
3400
3175
 
3401
 
    @needs_write_lock
3402
3176
    def push(self, overwrite=False, stop_revision=None,
3403
3177
             _override_hook_source_branch=None):
3404
3178
        """Mirror the source branch into the target branch.
3407
3181
        """
3408
3182
        raise NotImplementedError(self.push)
3409
3183
 
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
 
 
3419
3184
 
3420
3185
class GenericInterBranch(InterBranch):
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
 
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
 
3459
3193
    def update_revisions(self, stop_revision=None, overwrite=False,
3460
 
        graph=None, fetch_tags=True):
 
3194
        graph=None):
3461
3195
        """See InterBranch.update_revisions()."""
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
 
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
 
3510
3236
    def pull(self, overwrite=False, stop_revision=None,
3511
 
             possible_transports=None, run_hooks=True,
 
3237
             possible_transports=None, _hook_master=None, run_hooks=True,
3512
3238
             _override_hook_target=None, local=False):
3513
 
        """Pull from source into self, updating my master if any.
 
3239
        """See Branch.pull.
3514
3240
 
 
3241
        :param _hook_master: Private parameter - set the branch to
 
3242
            be supplied as the master to pull hooks.
3515
3243
        :param run_hooks: Private parameter - if false, this branch
3516
3244
            is being called because it's the master of the primary branch,
3517
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.
3518
3249
        """
3519
 
        bound_location = self.target.get_bound_location()
3520
 
        if local and not bound_location:
 
3250
        # This type of branch can't be bound.
 
3251
        if local:
3521
3252
            raise errors.LocalRequiresBoundBranch()
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()
 
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()
3528
3260
        try:
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)
 
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)
3538
3287
        finally:
3539
 
            if master_branch:
3540
 
                master_branch.unlock()
 
3288
            self.source.unlock()
 
3289
        return result
3541
3290
 
3542
3291
    def push(self, overwrite=False, stop_revision=None,
3543
3292
             _override_hook_source_branch=None):
3583
3332
                # push into the master from the source branch.
3584
3333
                self.source._basic_push(master_branch, overwrite, stop_revision)
3585
3334
                # and push into the target branch from the source. Note that we
3586
 
                # push from the source branch again, because it's considered the
 
3335
                # push from the source branch again, because its considered the
3587
3336
                # highest bandwidth repository.
3588
3337
                result = self.source._basic_push(self.target, overwrite,
3589
3338
                    stop_revision)
3605
3354
            _run_hooks()
3606
3355
            return result
3607
3356
 
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.
 
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
 
3619
3374
        :param run_hooks: Private parameter - if false, this branch
3620
3375
            is being called because it's the master of the primary branch,
3621
3376
            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.
3625
3377
        """
3626
 
        # This type of branch can't be bound.
3627
 
        if local:
 
3378
        bound_location = self.target.get_bound_location()
 
3379
        if local and not bound_location:
3628
3380
            raise errors.LocalRequiresBoundBranch()
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()
 
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()
3636
3386
        try:
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)
 
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)
3663
3395
        finally:
3664
 
            self.source.unlock()
3665
 
        return result
 
3396
            if master_branch:
 
3397
                master_branch.unlock()
3666
3398
 
3667
3399
 
3668
3400
InterBranch.register_optimiser(GenericInterBranch)
 
3401
InterBranch.register_optimiser(InterToBranch5)