749
801
        if not self._format.supports_stacking():
 
750
 
            raise errors.UnstackableBranchFormat(self._format, self.user_url)
 
751
 
        # XXX: Changing from one fallback repository to another does not check
 
752
 
        # that all the data you need is present in the new fallback.
 
753
 
        # Possibly it should.
 
754
 
        self._check_stackable_repo()
 
757
 
                old_url = self.get_stacked_on_url()
 
758
 
            except (errors.NotStacked, errors.UnstackableBranchFormat,
 
759
 
                errors.UnstackableRepositoryFormat):
 
763
 
            self._activate_fallback_location(url)
 
764
 
        # write this out after the repository is stacked to avoid setting a
 
765
 
        # stacked config that doesn't work.
 
766
 
        self._set_config_location('stacked_on_location', url)
 
 
802
            raise UnstackableBranchFormat(self._format, self.user_url)
 
 
803
        with self.lock_write():
 
 
804
            # XXX: Changing from one fallback repository to another does not
 
 
805
            # check that all the data you need is present in the new fallback.
 
 
806
            # Possibly it should.
 
 
807
            self._check_stackable_repo()
 
 
810
                    self.get_stacked_on_url()
 
 
811
                except (errors.NotStacked, UnstackableBranchFormat,
 
 
812
                        errors.UnstackableRepositoryFormat):
 
 
816
                self._activate_fallback_location(
 
 
817
                    url, possible_transports=[self.controldir.root_transport])
 
 
818
            # write this out after the repository is stacked to avoid setting a
 
 
819
            # stacked config that doesn't work.
 
 
820
            self._set_config_location('stacked_on_location', url)
 
768
822
    def _unstack(self):
 
769
823
        """Change a branch to be unstacked, copying data as needed.
 
771
825
        Don't call this directly, use set_stacked_on_url(None).
 
773
 
        pb = ui.ui_factory.nested_progress_bar()
 
775
 
            pb.update("Unstacking")
 
 
827
        with ui.ui_factory.nested_progress_bar() as pb:
 
 
828
            pb.update(gettext("Unstacking"))
 
776
829
            # The basic approach here is to fetch the tip of the branch,
 
777
830
            # including all available ghosts, from the existing stacked
 
778
 
            # repository into a new repository object without the fallbacks. 
 
 
831
            # repository into a new repository object without the fallbacks.
 
780
833
            # XXX: See <https://launchpad.net/bugs/397286> - this may not be
 
781
834
            # correct for CHKMap repostiories
 
782
835
            old_repository = self.repository
 
783
836
            if len(old_repository._fallback_repositories) != 1:
 
784
 
                raise AssertionError("can't cope with fallback repositories "
 
785
 
                    "of %r" % (self.repository,))
 
786
 
            # unlock it, including unlocking the fallback
 
 
837
                raise AssertionError(
 
 
838
                    "can't cope with fallback repositories "
 
 
839
                    "of %r (fallbacks: %r)" % (
 
 
840
                        old_repository, old_repository._fallback_repositories))
 
 
841
            # Open the new repository object.
 
 
842
            # Repositories don't offer an interface to remove fallback
 
 
843
            # repositories today; take the conceptually simpler option and just
 
 
844
            # reopen it.  We reopen it starting from the URL so that we
 
 
845
            # get a separate connection for RemoteRepositories and can
 
 
846
            # stream from one of them to the other.  This does mean doing
 
 
847
            # separate SSH connection setup, but unstacking is not a
 
 
848
            # common operation so it's tolerable.
 
 
849
            new_bzrdir = controldir.ControlDir.open(
 
 
850
                self.controldir.root_transport.base)
 
 
851
            new_repository = new_bzrdir.find_repository()
 
 
852
            if new_repository._fallback_repositories:
 
 
853
                raise AssertionError(
 
 
854
                    "didn't expect %r to have fallback_repositories"
 
 
855
                    % (self.repository,))
 
 
856
            # Replace self.repository with the new repository.
 
 
857
            # Do our best to transfer the lock state (i.e. lock-tokens and
 
 
858
            # lock count) of self.repository to the new repository.
 
 
859
            lock_token = old_repository.lock_write().repository_token
 
 
860
            self.repository = new_repository
 
 
861
            if isinstance(self, remote.RemoteBranch):
 
 
862
                # Remote branches can have a second reference to the old
 
 
863
                # repository that need to be replaced.
 
 
864
                if self._real_branch is not None:
 
 
865
                    self._real_branch.repository = new_repository
 
 
866
            self.repository.lock_write(token=lock_token)
 
 
867
            if lock_token is not None:
 
 
868
                old_repository.leave_lock_in_place()
 
787
869
            old_repository.unlock()
 
788
 
            old_repository.lock_read()
 
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
 
 
870
            if lock_token is not None:
 
 
871
                # XXX: self.repository.leave_lock_in_place() before this
 
 
872
                # function will not be preserved.  Fortunately that doesn't
 
 
873
                # affect the current default format (2a), and would be a
 
 
874
                # corner-case anyway.
 
 
875
                #  - Andrew Bennetts, 2010/06/30
 
 
876
                self.repository.dont_leave_lock_in_place()
 
 
880
                    old_repository.unlock()
 
 
881
                except errors.LockNotHeld:
 
 
884
            if old_lock_count == 0:
 
 
885
                raise AssertionError(
 
 
886
                    'old_repository should have been locked at least once.')
 
 
887
            for i in range(old_lock_count - 1):
 
807
888
                self.repository.lock_write()
 
 
889
            # Fetch from the old repository into the new.
 
 
890
            with old_repository.lock_read():
 
808
891
                # XXX: If you unstack a branch while it has a working tree
 
809
892
                # with a pending merge, the pending-merged revisions will no
 
810
893
                # longer be present.  You can (probably) revert and remerge.
 
812
 
                # XXX: This only fetches up to the tip of the repository; it
 
813
 
                # doesn't bring across any tags.  That's fairly consistent
 
814
 
                # with how branch works, but perhaps not ideal.
 
815
 
                self.repository.fetch(old_repository,
 
816
 
                    revision_id=self.last_revision(),
 
819
 
                old_repository.unlock()
 
823
 
    def _set_tags_bytes(self, bytes):
 
824
 
        """Mirror method for _get_tags_bytes.
 
826
 
        :seealso: Branch._get_tags_bytes.
 
828
 
        return _run_with_write_locked_target(self, self._transport.put_bytes,
 
 
895
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
 
 
896
                except errors.TagsNotSupported:
 
 
897
                    tags_to_fetch = set()
 
 
898
                fetch_spec = vf_search.NotInOtherForRevs(
 
 
899
                    self.repository, old_repository,
 
 
900
                    required_ids=[self.last_revision()],
 
 
901
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
 
 
902
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
 
831
904
    def _cache_revision_history(self, rev_history):
 
832
905
        """Set the cached revision history to rev_history.
 
 
910
978
        """Return last revision id, or NULL_REVISION."""
 
911
979
        return self.last_revision_info()[1]
 
914
981
    def last_revision_info(self):
 
915
982
        """Return information about the last revision.
 
917
984
        :return: A tuple (revno, revision_id).
 
919
 
        if self._last_revision_info_cache is None:
 
920
 
            self._last_revision_info_cache = self._last_revision_info()
 
921
 
        return self._last_revision_info_cache
 
923
 
    def _last_revision_info(self):
 
924
 
        rh = self.revision_history()
 
927
 
            return (revno, rh[-1])
 
929
 
            return (0, _mod_revision.NULL_REVISION)
 
931
 
    @deprecated_method(deprecated_in((1, 6, 0)))
 
932
 
    def missing_revisions(self, other, stop_revision=None):
 
933
 
        """Return a list of new revisions that would perfectly fit.
 
935
 
        If self and other have not diverged, return a list of the revisions
 
936
 
        present in other, but missing from self.
 
938
 
        self_history = self.revision_history()
 
939
 
        self_len = len(self_history)
 
940
 
        other_history = other.revision_history()
 
941
 
        other_len = len(other_history)
 
942
 
        common_index = min(self_len, other_len) -1
 
943
 
        if common_index >= 0 and \
 
944
 
            self_history[common_index] != other_history[common_index]:
 
945
 
            raise errors.DivergedBranches(self, other)
 
947
 
        if stop_revision is None:
 
948
 
            stop_revision = other_len
 
950
 
            if stop_revision > other_len:
 
951
 
                raise errors.NoSuchRevision(self, stop_revision)
 
952
 
        return other_history[self_len:stop_revision]
 
955
 
    def update_revisions(self, other, stop_revision=None, overwrite=False,
 
957
 
        """Pull in new perfect-fit revisions.
 
959
 
        :param other: Another Branch to pull from
 
960
 
        :param stop_revision: Updated until the given revision
 
961
 
        :param overwrite: Always set the branch pointer, rather than checking
 
962
 
            to see if it is a proper descendant.
 
963
 
        :param graph: A Graph object that can be used to query history
 
964
 
            information. This can be None.
 
967
 
        return InterBranch.get(other, self).update_revisions(stop_revision,
 
970
 
    def import_last_revision_info(self, source_repo, revno, revid):
 
 
986
        with self.lock_read():
 
 
987
            if self._last_revision_info_cache is None:
 
 
988
                self._last_revision_info_cache = (
 
 
989
                    self._read_last_revision_info())
 
 
990
            return self._last_revision_info_cache
 
 
992
    def _read_last_revision_info(self):
 
 
993
        raise NotImplementedError(self._read_last_revision_info)
 
 
995
    def import_last_revision_info_and_tags(self, source, revno, revid,
 
971
997
        """Set the last revision info, importing from another repo if necessary.
 
973
999
        This is used by the bound branch code to upload a revision to
 
974
1000
        the master branch first before updating the tip of the local branch.
 
 
1001
        Revisions referenced by source's tags are also transferred.
 
976
 
        :param source_repo: Source repository to optionally fetch from
 
 
1003
        :param source: Source branch to optionally fetch from
 
977
1004
        :param revno: Revision number of the new tip
 
978
1005
        :param revid: Revision id of the new tip
 
 
1006
        :param lossy: Whether to discard metadata that can not be
 
 
1007
            natively represented
 
 
1008
        :return: Tuple with the new revision number and revision id
 
 
1009
            (should only be different from the arguments when lossy=True)
 
980
 
        if not self.repository.has_same_location(source_repo):
 
981
 
            self.repository.fetch(source_repo, revision_id=revid)
 
 
1011
        if not self.repository.has_same_location(source.repository):
 
 
1012
            self.fetch(source, revid)
 
982
1013
        self.set_last_revision_info(revno, revid)
 
 
1014
        return (revno, revid)
 
984
1016
    def revision_id_to_revno(self, revision_id):
 
985
1017
        """Given a revision id, return its revno"""
 
986
1018
        if _mod_revision.is_null(revision_id):
 
988
 
        history = self.revision_history()
 
 
1020
        history = self._revision_history()
 
990
1022
            return history.index(revision_id) + 1
 
991
1023
        except ValueError:
 
992
1024
            raise errors.NoSuchRevision(self, revision_id)
 
995
1026
    def get_rev_id(self, revno, history=None):
 
996
1027
        """Find the revision id of the specified revno."""
 
998
 
            return _mod_revision.NULL_REVISION
 
999
 
        last_revno, last_revid = self.last_revision_info()
 
1000
 
        if revno == last_revno:
 
1002
 
        if revno <= 0 or revno > last_revno:
 
1003
 
            raise errors.NoSuchRevision(self, revno)
 
1004
 
        distance_from_last = last_revno - revno
 
1005
 
        if len(self._partial_revision_history_cache) <= distance_from_last:
 
1006
 
            self._extend_partial_history(distance_from_last)
 
1007
 
        return self._partial_revision_history_cache[distance_from_last]
 
 
1028
        with self.lock_read():
 
 
1030
                return _mod_revision.NULL_REVISION
 
 
1031
            last_revno, last_revid = self.last_revision_info()
 
 
1032
            if revno == last_revno:
 
 
1034
            if revno <= 0 or revno > last_revno:
 
 
1035
                raise errors.NoSuchRevision(self, revno)
 
 
1036
            distance_from_last = last_revno - revno
 
 
1037
            if len(self._partial_revision_history_cache) <= distance_from_last:
 
 
1038
                self._extend_partial_history(distance_from_last)
 
 
1039
            return self._partial_revision_history_cache[distance_from_last]
 
1010
1041
    def pull(self, source, overwrite=False, stop_revision=None,
 
1011
1042
             possible_transports=None, *args, **kwargs):
 
1012
1043
        """Mirror source into this branch.
 
 
1347
1333
        # XXX: Fix the bzrdir API to allow getting the branch back from the
 
1348
1334
        # clone call. Or something. 20090224 RBC/spiv.
 
 
1335
        # XXX: Should this perhaps clone colocated branches as well,
 
 
1336
        # rather than just the default branch? 20100319 JRV
 
1349
1337
        if revision_id is None:
 
1350
1338
            revision_id = self.last_revision()
 
1351
 
        dir_to = self.bzrdir.clone_on_transport(to_transport,
 
1352
 
            revision_id=revision_id, stacked_on=stacked_on,
 
1353
 
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
 
 
1339
        dir_to = self.controldir.clone_on_transport(
 
 
1340
            to_transport, revision_id=revision_id, stacked_on=stacked_on,
 
 
1341
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
 
 
1342
            no_tree=no_tree, tag_selector=tag_selector)
 
1354
1343
        return dir_to.open_branch()
 
1356
1345
    def create_checkout(self, to_location, revision_id=None,
 
1357
1346
                        lightweight=False, accelerator_tree=None,
 
 
1347
                        hardlink=False, recurse_nested=True):
 
1359
1348
        """Create a checkout of a branch.
 
1361
1350
        :param to_location: The url to produce the checkout at
 
1362
1351
        :param revision_id: The revision to check out
 
1363
1352
        :param lightweight: If True, produce a lightweight checkout, otherwise,
 
1364
 
        produce a bound branch (heavyweight checkout)
 
 
1353
            produce a bound branch (heavyweight checkout)
 
1365
1354
        :param accelerator_tree: A tree which can be used for retrieving file
 
1366
1355
            contents more quickly than the revision tree, i.e. a workingtree.
 
1367
1356
            The revision tree will be used for cases where accelerator_tree's
 
1368
1357
            content is different.
 
1369
1358
        :param hardlink: If true, hard-link files from accelerator_tree,
 
1370
1359
            where possible.
 
 
1360
        :param recurse_nested: Whether to recurse into nested trees
 
1371
1361
        :return: The tree of the created checkout
 
1373
1363
        t = transport.get_transport(to_location)
 
1374
1364
        t.ensure_base()
 
 
1365
        format = self._get_checkout_format(lightweight=lightweight)
 
 
1367
            checkout = format.initialize_on_transport(t)
 
 
1368
        except errors.AlreadyControlDirError:
 
 
1369
            # It's fine if the control directory already exists,
 
 
1370
            # as long as there is no existing branch and working tree.
 
 
1371
            checkout = controldir.ControlDir.open_from_transport(t)
 
 
1373
                checkout.open_branch()
 
 
1374
            except errors.NotBranchError:
 
 
1377
                raise errors.AlreadyControlDirError(t.base)
 
 
1378
            if (checkout.control_transport.base
 
 
1379
                    == self.controldir.control_transport.base):
 
 
1380
                # When checking out to the same control directory,
 
 
1381
                # always create a lightweight checkout
 
1375
1384
        if lightweight:
 
1376
 
            format = self._get_checkout_format()
 
1377
 
            checkout = format.initialize_on_transport(t)
 
1378
 
            from_branch = BranchReferenceFormat().initialize(checkout, 
 
 
1385
            from_branch = checkout.set_branch_reference(target_branch=self)
 
1381
 
            format = self._get_checkout_format()
 
1382
 
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
 
1383
 
                to_location, force_new_tree=False, format=format)
 
1384
 
            checkout = checkout_branch.bzrdir
 
 
1387
            policy = checkout.determine_repository_policy()
 
 
1388
            policy.acquire_repository()
 
 
1389
            checkout_branch = checkout.create_branch()
 
1385
1390
            checkout_branch.bind(self)
 
1386
1391
            # pull up to the specified revision_id to set the initial
 
1387
1392
            # branch tip correctly, and seed it with history.
 
1388
1393
            checkout_branch.pull(self, stop_revision=revision_id)
 
1390
1395
        tree = checkout.create_workingtree(revision_id,
 
1391
1396
                                           from_branch=from_branch,
 
1392
1397
                                           accelerator_tree=accelerator_tree,
 
1393
1398
                                           hardlink=hardlink)
 
1394
1399
        basis_tree = tree.basis_tree()
 
1395
 
        basis_tree.lock_read()
 
1397
 
            for path, file_id in basis_tree.iter_references():
 
1398
 
                reference_parent = self.reference_parent(file_id, path)
 
1399
 
                reference_parent.create_checkout(tree.abspath(path),
 
1400
 
                    basis_tree.get_reference_revision(file_id, path),
 
 
1400
        with basis_tree.lock_read():
 
 
1401
            for path in basis_tree.iter_references():
 
 
1402
                reference_parent = tree.reference_parent(path)
 
 
1403
                if reference_parent is None:
 
 
1404
                    warning('Branch location for %s unknown.', path)
 
 
1406
                reference_parent.create_checkout(
 
 
1408
                    basis_tree.get_reference_revision(path), lightweight)
 
1407
1411
    def reconcile(self, thorough=True):
 
1408
 
        """Make sure the data stored in this branch is consistent."""
 
1409
 
        from bzrlib.reconcile import BranchReconciler
 
1410
 
        reconciler = BranchReconciler(self, thorough=thorough)
 
1411
 
        reconciler.reconcile()
 
 
1412
        """Make sure the data stored in this branch is consistent.
 
1414
 
    def reference_parent(self, file_id, path, possible_transports=None):
 
1415
 
        """Return the parent branch for a tree-reference file_id
 
1416
 
        :param file_id: The file_id of the tree reference
 
1417
 
        :param path: The path of the file_id in the tree
 
1418
 
        :return: A branch associated with the file_id
 
 
1414
        :return: A `ReconcileResult` object.
 
1420
 
        # FIXME should provide multiple branches, based on config
 
1421
 
        return Branch.open(self.bzrdir.root_transport.clone(path).base,
 
1422
 
                           possible_transports=possible_transports)
 
 
1416
        raise NotImplementedError(self.reconcile)
 
1424
1418
    def supports_tags(self):
 
1425
1419
        return self._format.supports_tags()
 
 
1490
1513
    object will be created every time regardless.
 
1493
 
    _default_format = None
 
1494
 
    """The default format used for new branches."""
 
1497
 
    """The known formats."""
 
1499
 
    can_set_append_revisions_only = True
 
1501
1516
    def __eq__(self, other):
 
1502
1517
        return self.__class__ is other.__class__
 
1504
1519
    def __ne__(self, other):
 
1505
1520
        return not (self == other)
 
1508
 
    def find_format(klass, a_bzrdir, name=None):
 
1509
 
        """Return the format for the branch object in a_bzrdir."""
 
1511
 
            transport = a_bzrdir.get_branch_transport(None, name=name)
 
1512
 
            format_string = transport.get_bytes("format")
 
1513
 
            return klass._formats[format_string]
 
1514
 
        except errors.NoSuchFile:
 
1515
 
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
 
1517
 
            raise errors.UnknownFormatError(format=format_string, kind='branch')
 
1520
 
    def get_default_format(klass):
 
1521
 
        """Return the current default format."""
 
1522
 
        return klass._default_format
 
1524
 
    def get_reference(self, a_bzrdir):
 
1525
 
        """Get the target reference of the branch in a_bzrdir.
 
 
1522
    def get_reference(self, controldir, name=None):
 
 
1523
        """Get the target reference of the branch in controldir.
 
1527
1525
        format probing must have been completed before calling
 
1528
1526
        this method - it is assumed that the format of the branch
 
1529
 
        in a_bzrdir is correct.
 
 
1527
        in controldir is correct.
 
1531
 
        :param a_bzrdir: The bzrdir to get the branch data from.
 
 
1529
        :param controldir: The controldir to get the branch data from.
 
 
1530
        :param name: Name of the colocated branch to fetch
 
1532
1531
        :return: None if the branch is not a reference branch.
 
1537
 
    def set_reference(self, a_bzrdir, to_branch):
 
1538
 
        """Set the target reference of the branch in a_bzrdir.
 
 
1536
    def set_reference(self, controldir, name, to_branch):
 
 
1537
        """Set the target reference of the branch in controldir.
 
1540
1539
        format probing must have been completed before calling
 
1541
1540
        this method - it is assumed that the format of the branch
 
1542
 
        in a_bzrdir is correct.
 
 
1541
        in controldir is correct.
 
1544
 
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
 
1543
        :param controldir: The controldir to set the branch reference for.
 
 
1544
        :param name: Name of colocated branch to set, None for default
 
1545
1545
        :param to_branch: branch that the checkout is to reference
 
1547
1547
        raise NotImplementedError(self.set_reference)
 
1549
 
    def get_format_string(self):
 
1550
 
        """Return the ASCII format string that identifies this format."""
 
1551
 
        raise NotImplementedError(self.get_format_string)
 
1553
1549
    def get_format_description(self):
 
1554
1550
        """Return the short format description for this format."""
 
1555
1551
        raise NotImplementedError(self.get_format_description)
 
1557
 
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
 
 
1553
    def _run_post_branch_init_hooks(self, controldir, name, branch):
 
1558
1554
        hooks = Branch.hooks['post_branch_init']
 
1561
 
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
 
 
1557
        params = BranchInitHookParams(self, controldir, name, branch)
 
1562
1558
        for hook in hooks:
 
1565
 
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
1566
 
                           lock_type='metadir', set_format=True):
 
1567
 
        """Initialize a branch in a bzrdir, with specified files
 
1569
 
        :param a_bzrdir: The bzrdir to initialize the branch in
 
1570
 
        :param utf8_files: The files to create as a list of
 
1571
 
            (filename, content) tuples
 
1572
 
        :param name: Name of colocated branch to create, if any
 
1573
 
        :param set_format: If True, set the format with
 
1574
 
            self.get_format_string.  (BzrBranch4 has its format set
 
1576
 
        :return: a branch in this format
 
1578
 
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
1579
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
 
1581
 
            'metadir': ('lock', lockdir.LockDir),
 
1582
 
            'branch4': ('branch-lock', lockable_files.TransportLock),
 
1584
 
        lock_name, lock_class = lock_map[lock_type]
 
1585
 
        control_files = lockable_files.LockableFiles(branch_transport,
 
1586
 
            lock_name, lock_class)
 
1587
 
        control_files.create_lock()
 
1589
 
            control_files.lock_write()
 
1590
 
        except errors.LockContention:
 
1591
 
            if lock_type != 'branch4':
 
1597
 
            utf8_files += [('format', self.get_format_string())]
 
1599
 
            for (filename, content) in utf8_files:
 
1600
 
                branch_transport.put_bytes(
 
1602
 
                    mode=a_bzrdir._get_file_mode())
 
1605
 
                control_files.unlock()
 
1606
 
        branch = self.open(a_bzrdir, name, _found=True)
 
1607
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
1610
 
    def initialize(self, a_bzrdir, name=None):
 
1611
 
        """Create a branch of this format in a_bzrdir.
 
 
1561
    def initialize(self, controldir, name=None, repository=None,
 
 
1562
                   append_revisions_only=None):
 
 
1563
        """Create a branch of this format in controldir.
 
1613
1565
        :param name: Name of the colocated branch to create.
 
1615
1567
        raise NotImplementedError(self.initialize)
 
 
1899
1856
        return self.__dict__ == other.__dict__
 
1901
1858
    def __repr__(self):
 
1902
 
        return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
 
1903
 
            self.control_dir, self.to_branch,
 
 
1859
        return "<%s for %s to (%s, %s)>" % (
 
 
1860
            self.__class__.__name__, self.control_dir, self.to_branch,
 
1904
1861
            self.revision_id)
 
1907
 
class BzrBranchFormat4(BranchFormat):
 
1908
 
    """Bzr branch format 4.
 
1911
 
     - a revision-history file.
 
1912
 
     - a branch-lock lock file [ to be shared with the bzrdir ]
 
1915
 
    def get_format_description(self):
 
1916
 
        """See BranchFormat.get_format_description()."""
 
1917
 
        return "Branch format 4"
 
1919
 
    def initialize(self, a_bzrdir, name=None):
 
1920
 
        """Create a branch of this format in a_bzrdir."""
 
1921
 
        utf8_files = [('revision-history', ''),
 
1922
 
                      ('branch-name', ''),
 
1924
 
        return self._initialize_helper(a_bzrdir, utf8_files, name=name,
 
1925
 
                                       lock_type='branch4', set_format=False)
 
1928
 
        super(BzrBranchFormat4, self).__init__()
 
1929
 
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
 
1931
 
    def network_name(self):
 
1932
 
        """The network name for this format is the control dirs disk label."""
 
1933
 
        return self._matchingbzrdir.get_format_string()
 
1935
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
1936
 
        """See BranchFormat.open()."""
 
1938
 
            # we are being called directly and must probe.
 
1939
 
            raise NotImplementedError
 
1940
 
        return BzrBranch(_format=self,
 
1941
 
                         _control_files=a_bzrdir._control_files,
 
1944
 
                         _repository=a_bzrdir.open_repository())
 
1947
 
        return "Bazaar-NG branch format 4"
 
1950
 
class BranchFormatMetadir(BranchFormat):
 
1951
 
    """Common logic for meta-dir based branch formats."""
 
1953
 
    def _branch_class(self):
 
1954
 
        """What class to instantiate on open calls."""
 
1955
 
        raise NotImplementedError(self._branch_class)
 
1957
 
    def network_name(self):
 
1958
 
        """A simple byte string uniquely identifying this format for RPC calls.
 
1960
 
        Metadir branch formats use their format string.
 
1962
 
        return self.get_format_string()
 
1964
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
1965
 
        """See BranchFormat.open()."""
 
1967
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
1968
 
            if format.__class__ != self.__class__:
 
1969
 
                raise AssertionError("wrong format %r found for %r" %
 
1971
 
        transport = a_bzrdir.get_branch_transport(None, name=name)
 
1973
 
            control_files = lockable_files.LockableFiles(transport, 'lock',
 
1975
 
            return self._branch_class()(_format=self,
 
1976
 
                              _control_files=control_files,
 
1979
 
                              _repository=a_bzrdir.find_repository(),
 
1980
 
                              ignore_fallbacks=ignore_fallbacks)
 
1981
 
        except errors.NoSuchFile:
 
1982
 
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
 
1985
 
        super(BranchFormatMetadir, self).__init__()
 
1986
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
1987
 
        self._matchingbzrdir.set_branch_format(self)
 
1989
 
    def supports_tags(self):
 
1993
 
class BzrBranchFormat5(BranchFormatMetadir):
 
1994
 
    """Bzr branch format 5.
 
1997
 
     - a revision-history file.
 
1999
 
     - a lock dir guarding the branch itself
 
2000
 
     - all of this stored in a branch/ subdirectory
 
2001
 
     - works with shared repositories.
 
2003
 
    This format is new in bzr 0.8.
 
2006
 
    def _branch_class(self):
 
2009
 
    def get_format_string(self):
 
2010
 
        """See BranchFormat.get_format_string()."""
 
2011
 
        return "Bazaar-NG branch format 5\n"
 
2013
 
    def get_format_description(self):
 
2014
 
        """See BranchFormat.get_format_description()."""
 
2015
 
        return "Branch format 5"
 
2017
 
    def initialize(self, a_bzrdir, name=None):
 
2018
 
        """Create a branch of this format in a_bzrdir."""
 
2019
 
        utf8_files = [('revision-history', ''),
 
2020
 
                      ('branch-name', ''),
 
2022
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2024
 
    def supports_tags(self):
 
2028
 
class BzrBranchFormat6(BranchFormatMetadir):
 
2029
 
    """Branch format with last-revision and tags.
 
2031
 
    Unlike previous formats, this has no explicit revision history. Instead,
 
2032
 
    this just stores the last-revision, and the left-hand history leading
 
2033
 
    up to there is the history.
 
2035
 
    This format was introduced in bzr 0.15
 
2036
 
    and became the default in 0.91.
 
2039
 
    def _branch_class(self):
 
2042
 
    def get_format_string(self):
 
2043
 
        """See BranchFormat.get_format_string()."""
 
2044
 
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
 
2046
 
    def get_format_description(self):
 
2047
 
        """See BranchFormat.get_format_description()."""
 
2048
 
        return "Branch format 6"
 
2050
 
    def initialize(self, a_bzrdir, name=None):
 
2051
 
        """Create a branch of this format in a_bzrdir."""
 
2052
 
        utf8_files = [('last-revision', '0 null:\n'),
 
2053
 
                      ('branch.conf', ''),
 
2056
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2058
 
    def make_tags(self, branch):
 
2059
 
        """See bzrlib.branch.BranchFormat.make_tags()."""
 
2060
 
        return BasicTags(branch)
 
2062
 
    def supports_set_append_revisions_only(self):
 
2066
 
class BzrBranchFormat8(BranchFormatMetadir):
 
2067
 
    """Metadir format supporting storing locations of subtree branches."""
 
2069
 
    def _branch_class(self):
 
2072
 
    def get_format_string(self):
 
2073
 
        """See BranchFormat.get_format_string()."""
 
2074
 
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
 
2076
 
    def get_format_description(self):
 
2077
 
        """See BranchFormat.get_format_description()."""
 
2078
 
        return "Branch format 8"
 
2080
 
    def initialize(self, a_bzrdir, name=None):
 
2081
 
        """Create a branch of this format in a_bzrdir."""
 
2082
 
        utf8_files = [('last-revision', '0 null:\n'),
 
2083
 
                      ('branch.conf', ''),
 
2087
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2090
 
        super(BzrBranchFormat8, self).__init__()
 
2091
 
        self._matchingbzrdir.repository_format = \
 
2092
 
            RepositoryFormatKnitPack5RichRoot()
 
2094
 
    def make_tags(self, branch):
 
2095
 
        """See bzrlib.branch.BranchFormat.make_tags()."""
 
2096
 
        return BasicTags(branch)
 
2098
 
    def supports_set_append_revisions_only(self):
 
2101
 
    def supports_stacking(self):
 
2104
 
    supports_reference_locations = True
 
2107
 
class BzrBranchFormat7(BzrBranchFormat8):
 
2108
 
    """Branch format with last-revision, tags, and a stacked location pointer.
 
2110
 
    The stacked location pointer is passed down to the repository and requires
 
2111
 
    a repository format with supports_external_lookups = True.
 
2113
 
    This format was introduced in bzr 1.6.
 
2116
 
    def initialize(self, a_bzrdir, name=None):
 
2117
 
        """Create a branch of this format in a_bzrdir."""
 
2118
 
        utf8_files = [('last-revision', '0 null:\n'),
 
2119
 
                      ('branch.conf', ''),
 
2122
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2124
 
    def _branch_class(self):
 
2127
 
    def get_format_string(self):
 
2128
 
        """See BranchFormat.get_format_string()."""
 
2129
 
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
 
2131
 
    def get_format_description(self):
 
2132
 
        """See BranchFormat.get_format_description()."""
 
2133
 
        return "Branch format 7"
 
2135
 
    def supports_set_append_revisions_only(self):
 
2138
 
    supports_reference_locations = False
 
2141
 
class BranchReferenceFormat(BranchFormat):
 
2142
 
    """Bzr branch reference format.
 
2144
 
    Branch references are used in implementing checkouts, they
 
2145
 
    act as an alias to the real branch which is at some other url.
 
2152
 
    def get_format_string(self):
 
2153
 
        """See BranchFormat.get_format_string()."""
 
2154
 
        return "Bazaar-NG Branch Reference Format 1\n"
 
2156
 
    def get_format_description(self):
 
2157
 
        """See BranchFormat.get_format_description()."""
 
2158
 
        return "Checkout reference format 1"
 
2160
 
    def get_reference(self, a_bzrdir):
 
2161
 
        """See BranchFormat.get_reference()."""
 
2162
 
        transport = a_bzrdir.get_branch_transport(None)
 
2163
 
        return transport.get_bytes('location')
 
2165
 
    def set_reference(self, a_bzrdir, to_branch):
 
2166
 
        """See BranchFormat.set_reference()."""
 
2167
 
        transport = a_bzrdir.get_branch_transport(None)
 
2168
 
        location = transport.put_bytes('location', to_branch.base)
 
2170
 
    def initialize(self, a_bzrdir, name=None, target_branch=None):
 
2171
 
        """Create a branch of this format in a_bzrdir."""
 
2172
 
        if target_branch is None:
 
2173
 
            # this format does not implement branch itself, thus the implicit
 
2174
 
            # creation contract must see it as uninitializable
 
2175
 
            raise errors.UninitializableFormat(self)
 
2176
 
        mutter('creating branch reference in %s', a_bzrdir.user_url)
 
2177
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
 
2178
 
        branch_transport.put_bytes('location',
 
2179
 
            target_branch.bzrdir.user_url)
 
2180
 
        branch_transport.put_bytes('format', self.get_format_string())
 
2182
 
            a_bzrdir, name, _found=True,
 
2183
 
            possible_transports=[target_branch.bzrdir.root_transport])
 
2184
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
2188
 
        super(BranchReferenceFormat, self).__init__()
 
2189
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
2190
 
        self._matchingbzrdir.set_branch_format(self)
 
2192
 
    def _make_reference_clone_function(format, a_branch):
 
2193
 
        """Create a clone() routine for a branch dynamically."""
 
2194
 
        def clone(to_bzrdir, revision_id=None,
 
2195
 
            repository_policy=None):
 
2196
 
            """See Branch.clone()."""
 
2197
 
            return format.initialize(to_bzrdir, target_branch=a_branch)
 
2198
 
            # cannot obey revision_id limits when cloning a reference ...
 
2199
 
            # FIXME RBC 20060210 either nuke revision_id for clone, or
 
2200
 
            # emit some sort of warning/error to the caller ?!
 
2203
 
    def open(self, a_bzrdir, name=None, _found=False, location=None,
 
2204
 
             possible_transports=None, ignore_fallbacks=False):
 
2205
 
        """Return the branch that the branch reference in a_bzrdir points at.
 
2207
 
        :param a_bzrdir: A BzrDir that contains a branch.
 
2208
 
        :param name: Name of colocated branch to open, if any
 
2209
 
        :param _found: a private parameter, do not use it. It is used to
 
2210
 
            indicate if format probing has already be done.
 
2211
 
        :param ignore_fallbacks: when set, no fallback branches will be opened
 
2212
 
            (if there are any).  Default is to open fallbacks.
 
2213
 
        :param location: The location of the referenced branch.  If
 
2214
 
            unspecified, this will be determined from the branch reference in
 
2216
 
        :param possible_transports: An optional reusable transports list.
 
2219
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2220
 
            if format.__class__ != self.__class__:
 
2221
 
                raise AssertionError("wrong format %r found for %r" %
 
2223
 
        if location is None:
 
2224
 
            location = self.get_reference(a_bzrdir)
 
2225
 
        real_bzrdir = bzrdir.BzrDir.open(
 
2226
 
            location, possible_transports=possible_transports)
 
2227
 
        result = real_bzrdir.open_branch(name=name, 
 
2228
 
            ignore_fallbacks=ignore_fallbacks)
 
2229
 
        # this changes the behaviour of result.clone to create a new reference
 
2230
 
        # rather than a copy of the content of the branch.
 
2231
 
        # I did not use a proxy object because that needs much more extensive
 
2232
 
        # testing, and we are only changing one behaviour at the moment.
 
2233
 
        # If we decide to alter more behaviours - i.e. the implicit nickname
 
2234
 
        # then this should be refactored to introduce a tested proxy branch
 
2235
 
        # and a subclass of that for use in overriding clone() and ....
 
2237
 
        result.clone = self._make_reference_clone_function(result)
 
 
1864
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
 
 
1865
    """Branch format registry."""
 
 
1867
    def __init__(self, other_registry=None):
 
 
1868
        super(BranchFormatRegistry, self).__init__(other_registry)
 
 
1869
        self._default_format = None
 
 
1870
        self._default_format_key = None
 
 
1872
    def get_default(self):
 
 
1873
        """Return the current default format."""
 
 
1874
        if (self._default_format_key is not None
 
 
1875
                and self._default_format is None):
 
 
1876
            self._default_format = self.get(self._default_format_key)
 
 
1877
        return self._default_format
 
 
1879
    def set_default(self, format):
 
 
1880
        """Set the default format."""
 
 
1881
        self._default_format = format
 
 
1882
        self._default_format_key = None
 
 
1884
    def set_default_key(self, format_string):
 
 
1885
        """Set the default format by its format string."""
 
 
1886
        self._default_format_key = format_string
 
 
1887
        self._default_format = None
 
2241
1890
network_format_registry = registry.FormatRegistry()
 
 
2246
1895
BranchFormat.network_name() for more detail.
 
 
1898
format_registry = BranchFormatRegistry(network_format_registry)
 
2250
1901
# formats which have no format string are not discoverable
 
2251
1902
# and not independently creatable, so are not registered.
 
2252
 
__format5 = BzrBranchFormat5()
 
2253
 
__format6 = BzrBranchFormat6()
 
2254
 
__format7 = BzrBranchFormat7()
 
2255
 
__format8 = BzrBranchFormat8()
 
2256
 
BranchFormat.register_format(__format5)
 
2257
 
BranchFormat.register_format(BranchReferenceFormat())
 
2258
 
BranchFormat.register_format(__format6)
 
2259
 
BranchFormat.register_format(__format7)
 
2260
 
BranchFormat.register_format(__format8)
 
2261
 
BranchFormat.set_default_format(__format7)
 
2262
 
_legacy_formats = [BzrBranchFormat4(),
 
2264
 
network_format_registry.register(
 
2265
 
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
 
2268
 
class BzrBranch(Branch, _RelockDebugMixin):
 
2269
 
    """A branch stored in the actual filesystem.
 
2271
 
    Note that it's "local" in the context of the filesystem; it doesn't
 
2272
 
    really matter if it's on an nfs/smb/afs/coda/... share, as long as
 
2273
 
    it's writable, and can be accessed via the normal filesystem API.
 
2275
 
    :ivar _transport: Transport for file operations on this branch's
 
2276
 
        control files, typically pointing to the .bzr/branch directory.
 
2277
 
    :ivar repository: Repository for this branch.
 
2278
 
    :ivar base: The url of the base directory for this branch; the one
 
2279
 
        containing the .bzr directory.
 
2280
 
    :ivar name: Optional colocated branch name as it exists in the control
 
2284
 
    def __init__(self, _format=None,
 
2285
 
                 _control_files=None, a_bzrdir=None, name=None,
 
2286
 
                 _repository=None, ignore_fallbacks=False):
 
2287
 
        """Create new branch object at a particular location."""
 
2288
 
        if a_bzrdir is None:
 
2289
 
            raise ValueError('a_bzrdir must be supplied')
 
2291
 
            self.bzrdir = a_bzrdir
 
2292
 
        self._base = self.bzrdir.transport.clone('..').base
 
2294
 
        # XXX: We should be able to just do
 
2295
 
        #   self.base = self.bzrdir.root_transport.base
 
2296
 
        # but this does not quite work yet -- mbp 20080522
 
2297
 
        self._format = _format
 
2298
 
        if _control_files is None:
 
2299
 
            raise ValueError('BzrBranch _control_files is None')
 
2300
 
        self.control_files = _control_files
 
2301
 
        self._transport = _control_files._transport
 
2302
 
        self.repository = _repository
 
2303
 
        Branch.__init__(self)
 
2306
 
        if self.name is None:
 
2307
 
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
 
2309
 
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
 
2314
 
    def _get_base(self):
 
2315
 
        """Returns the directory containing the control directory."""
 
2318
 
    base = property(_get_base, doc="The URL for the root of this branch.")
 
2320
 
    def _get_config(self):
 
2321
 
        return TransportConfig(self._transport, 'branch.conf')
 
2323
 
    def is_locked(self):
 
2324
 
        return self.control_files.is_locked()
 
2326
 
    def lock_write(self, token=None):
 
2327
 
        if not self.is_locked():
 
2328
 
            self._note_lock('w')
 
2329
 
        # All-in-one needs to always unlock/lock.
 
2330
 
        repo_control = getattr(self.repository, 'control_files', None)
 
2331
 
        if self.control_files == repo_control or not self.is_locked():
 
2332
 
            self.repository._warn_if_deprecated(self)
 
2333
 
            self.repository.lock_write()
 
2338
 
            return self.control_files.lock_write(token=token)
 
2341
 
                self.repository.unlock()
 
2344
 
    def lock_read(self):
 
2345
 
        if not self.is_locked():
 
2346
 
            self._note_lock('r')
 
2347
 
        # All-in-one needs to always unlock/lock.
 
2348
 
        repo_control = getattr(self.repository, 'control_files', None)
 
2349
 
        if self.control_files == repo_control or not self.is_locked():
 
2350
 
            self.repository._warn_if_deprecated(self)
 
2351
 
            self.repository.lock_read()
 
2356
 
            self.control_files.lock_read()
 
2359
 
                self.repository.unlock()
 
2362
 
    @only_raises(errors.LockNotHeld, errors.LockBroken)
 
2365
 
            self.control_files.unlock()
 
2367
 
            # All-in-one needs to always unlock/lock.
 
2368
 
            repo_control = getattr(self.repository, 'control_files', None)
 
2369
 
            if (self.control_files == repo_control or
 
2370
 
                not self.control_files.is_locked()):
 
2371
 
                self.repository.unlock()
 
2372
 
            if not self.control_files.is_locked():
 
2373
 
                # we just released the lock
 
2374
 
                self._clear_cached_state()
 
2376
 
    def peek_lock_mode(self):
 
2377
 
        if self.control_files._lock_count == 0:
 
2380
 
            return self.control_files._lock_mode
 
2382
 
    def get_physical_lock_status(self):
 
2383
 
        return self.control_files.get_physical_lock_status()
 
2386
 
    def print_file(self, file, revision_id):
 
2387
 
        """See Branch.print_file."""
 
2388
 
        return self.repository.print_file(file, revision_id)
 
2390
 
    def _write_revision_history(self, history):
 
2391
 
        """Factored out of set_revision_history.
 
2393
 
        This performs the actual writing to disk.
 
2394
 
        It is intended to be called by BzrBranch5.set_revision_history."""
 
2395
 
        self._transport.put_bytes(
 
2396
 
            'revision-history', '\n'.join(history),
 
2397
 
            mode=self.bzrdir._get_file_mode())
 
2400
 
    def set_revision_history(self, rev_history):
 
2401
 
        """See Branch.set_revision_history."""
 
2402
 
        if 'evil' in debug.debug_flags:
 
2403
 
            mutter_callsite(3, "set_revision_history scales with history.")
 
2404
 
        check_not_reserved_id = _mod_revision.check_not_reserved_id
 
2405
 
        for rev_id in rev_history:
 
2406
 
            check_not_reserved_id(rev_id)
 
2407
 
        if Branch.hooks['post_change_branch_tip']:
 
2408
 
            # Don't calculate the last_revision_info() if there are no hooks
 
2410
 
            old_revno, old_revid = self.last_revision_info()
 
2411
 
        if len(rev_history) == 0:
 
2412
 
            revid = _mod_revision.NULL_REVISION
 
2414
 
            revid = rev_history[-1]
 
2415
 
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
 
2416
 
        self._write_revision_history(rev_history)
 
2417
 
        self._clear_cached_state()
 
2418
 
        self._cache_revision_history(rev_history)
 
2419
 
        for hook in Branch.hooks['set_rh']:
 
2420
 
            hook(self, rev_history)
 
2421
 
        if Branch.hooks['post_change_branch_tip']:
 
2422
 
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2424
 
    def _synchronize_history(self, destination, revision_id):
 
2425
 
        """Synchronize last revision and revision history between branches.
 
2427
 
        This version is most efficient when the destination is also a
 
2428
 
        BzrBranch5, but works for BzrBranch6 as long as the revision
 
2429
 
        history is the true lefthand parent history, and all of the revisions
 
2430
 
        are in the destination's repository.  If not, set_revision_history
 
2433
 
        :param destination: The branch to copy the history into
 
2434
 
        :param revision_id: The revision-id to truncate history at.  May
 
2435
 
          be None to copy complete history.
 
2437
 
        if not isinstance(destination._format, BzrBranchFormat5):
 
2438
 
            super(BzrBranch, self)._synchronize_history(
 
2439
 
                destination, revision_id)
 
2441
 
        if revision_id == _mod_revision.NULL_REVISION:
 
2444
 
            new_history = self.revision_history()
 
2445
 
        if revision_id is not None and new_history != []:
 
2447
 
                new_history = new_history[:new_history.index(revision_id) + 1]
 
2449
 
                rev = self.repository.get_revision(revision_id)
 
2450
 
                new_history = rev.get_history(self.repository)[1:]
 
2451
 
        destination.set_revision_history(new_history)
 
2454
 
    def set_last_revision_info(self, revno, revision_id):
 
2455
 
        """Set the last revision of this branch.
 
2457
 
        The caller is responsible for checking that the revno is correct
 
2458
 
        for this revision id.
 
2460
 
        It may be possible to set the branch last revision to an id not
 
2461
 
        present in the repository.  However, branches can also be
 
2462
 
        configured to check constraints on history, in which case this may not
 
2465
 
        revision_id = _mod_revision.ensure_null(revision_id)
 
2466
 
        # this old format stores the full history, but this api doesn't
 
2467
 
        # provide it, so we must generate, and might as well check it's
 
2469
 
        history = self._lefthand_history(revision_id)
 
2470
 
        if len(history) != revno:
 
2471
 
            raise AssertionError('%d != %d' % (len(history), revno))
 
2472
 
        self.set_revision_history(history)
 
2474
 
    def _gen_revision_history(self):
 
2475
 
        history = self._transport.get_bytes('revision-history').split('\n')
 
2476
 
        if history[-1:] == ['']:
 
2477
 
            # There shouldn't be a trailing newline, but just in case.
 
2482
 
    def generate_revision_history(self, revision_id, last_rev=None,
 
2484
 
        """Create a new revision history that will finish with revision_id.
 
2486
 
        :param revision_id: the new tip to use.
 
2487
 
        :param last_rev: The previous last_revision. If not None, then this
 
2488
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
2489
 
        :param other_branch: The other branch that DivergedBranches should
 
2490
 
            raise with respect to.
 
2492
 
        self.set_revision_history(self._lefthand_history(revision_id,
 
2493
 
            last_rev, other_branch))
 
2495
 
    def basis_tree(self):
 
2496
 
        """See Branch.basis_tree."""
 
2497
 
        return self.repository.revision_tree(self.last_revision())
 
2499
 
    def _get_parent_location(self):
 
2500
 
        _locs = ['parent', 'pull', 'x-pull']
 
2503
 
                return self._transport.get_bytes(l).strip('\n')
 
2504
 
            except errors.NoSuchFile:
 
2508
 
    def _basic_push(self, target, overwrite, stop_revision):
 
2509
 
        """Basic implementation of push without bound branches or hooks.
 
2511
 
        Must be called with source read locked and target write locked.
 
2513
 
        result = BranchPushResult()
 
2514
 
        result.source_branch = self
 
2515
 
        result.target_branch = target
 
2516
 
        result.old_revno, result.old_revid = target.last_revision_info()
 
2517
 
        self.update_references(target)
 
2518
 
        if result.old_revid != self.last_revision():
 
2519
 
            # We assume that during 'push' this repository is closer than
 
2521
 
            graph = self.repository.get_graph(target.repository)
 
2522
 
            target.update_revisions(self, stop_revision,
 
2523
 
                overwrite=overwrite, graph=graph)
 
2524
 
        if self._push_should_merge_tags():
 
2525
 
            result.tag_conflicts = self.tags.merge_to(target.tags,
 
2527
 
        result.new_revno, result.new_revid = target.last_revision_info()
 
2530
 
    def get_stacked_on_url(self):
 
2531
 
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
 
2533
 
    def set_push_location(self, location):
 
2534
 
        """See Branch.set_push_location."""
 
2535
 
        self.get_config().set_user_option(
 
2536
 
            'push_location', location,
 
2537
 
            store=_mod_config.STORE_LOCATION_NORECURSE)
 
2539
 
    def _set_parent_location(self, url):
 
2541
 
            self._transport.delete('parent')
 
2543
 
            self._transport.put_bytes('parent', url + '\n',
 
2544
 
                mode=self.bzrdir._get_file_mode())
 
2547
 
class BzrBranch5(BzrBranch):
 
2548
 
    """A format 5 branch. This supports new features over plain branches.
 
2550
 
    It has support for a master_branch which is the data for bound branches.
 
2553
 
    def get_bound_location(self):
 
2555
 
            return self._transport.get_bytes('bound')[:-1]
 
2556
 
        except errors.NoSuchFile:
 
2560
 
    def get_master_branch(self, possible_transports=None):
 
2561
 
        """Return the branch we are bound to.
 
2563
 
        :return: Either a Branch, or None
 
2565
 
        This could memoise the branch, but if thats done
 
2566
 
        it must be revalidated on each new lock.
 
2567
 
        So for now we just don't memoise it.
 
2568
 
        # RBC 20060304 review this decision.
 
2570
 
        bound_loc = self.get_bound_location()
 
2574
 
            return Branch.open(bound_loc,
 
2575
 
                               possible_transports=possible_transports)
 
2576
 
        except (errors.NotBranchError, errors.ConnectionError), e:
 
2577
 
            raise errors.BoundBranchConnectionFailure(
 
2581
 
    def set_bound_location(self, location):
 
2582
 
        """Set the target where this branch is bound to.
 
2584
 
        :param location: URL to the target branch
 
2587
 
            self._transport.put_bytes('bound', location+'\n',
 
2588
 
                mode=self.bzrdir._get_file_mode())
 
2591
 
                self._transport.delete('bound')
 
2592
 
            except errors.NoSuchFile:
 
2597
 
    def bind(self, other):
 
2598
 
        """Bind this branch to the branch other.
 
2600
 
        This does not push or pull data between the branches, though it does
 
2601
 
        check for divergence to raise an error when the branches are not
 
2602
 
        either the same, or one a prefix of the other. That behaviour may not
 
2603
 
        be useful, so that check may be removed in future.
 
2605
 
        :param other: The branch to bind to
 
2608
 
        # TODO: jam 20051230 Consider checking if the target is bound
 
2609
 
        #       It is debatable whether you should be able to bind to
 
2610
 
        #       a branch which is itself bound.
 
2611
 
        #       Committing is obviously forbidden,
 
2612
 
        #       but binding itself may not be.
 
2613
 
        #       Since we *have* to check at commit time, we don't
 
2614
 
        #       *need* to check here
 
2616
 
        # we want to raise diverged if:
 
2617
 
        # last_rev is not in the other_last_rev history, AND
 
2618
 
        # other_last_rev is not in our history, and do it without pulling
 
2620
 
        self.set_bound_location(other.base)
 
2624
 
        """If bound, unbind"""
 
2625
 
        return self.set_bound_location(None)
 
2628
 
    def update(self, possible_transports=None):
 
2629
 
        """Synchronise this branch with the master branch if any.
 
2631
 
        :return: None or the last_revision that was pivoted out during the
 
2634
 
        master = self.get_master_branch(possible_transports)
 
2635
 
        if master is not None:
 
2636
 
            old_tip = _mod_revision.ensure_null(self.last_revision())
 
2637
 
            self.pull(master, overwrite=True)
 
2638
 
            if self.repository.get_graph().is_ancestor(old_tip,
 
2639
 
                _mod_revision.ensure_null(self.last_revision())):
 
2645
 
class BzrBranch8(BzrBranch5):
 
2646
 
    """A branch that stores tree-reference locations."""
 
2648
 
    def _open_hook(self):
 
2649
 
        if self._ignore_fallbacks:
 
2652
 
            url = self.get_stacked_on_url()
 
2653
 
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
 
2654
 
            errors.UnstackableBranchFormat):
 
2657
 
            for hook in Branch.hooks['transform_fallback_location']:
 
2658
 
                url = hook(self, url)
 
2660
 
                    hook_name = Branch.hooks.get_hook_name(hook)
 
2661
 
                    raise AssertionError(
 
2662
 
                        "'transform_fallback_location' hook %s returned "
 
2663
 
                        "None, not a URL." % hook_name)
 
2664
 
            self._activate_fallback_location(url)
 
2666
 
    def __init__(self, *args, **kwargs):
 
2667
 
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
 
2668
 
        super(BzrBranch8, self).__init__(*args, **kwargs)
 
2669
 
        self._last_revision_info_cache = None
 
2670
 
        self._reference_info = None
 
2672
 
    def _clear_cached_state(self):
 
2673
 
        super(BzrBranch8, self)._clear_cached_state()
 
2674
 
        self._last_revision_info_cache = None
 
2675
 
        self._reference_info = None
 
2677
 
    def _last_revision_info(self):
 
2678
 
        revision_string = self._transport.get_bytes('last-revision')
 
2679
 
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
 
2680
 
        revision_id = cache_utf8.get_cached_utf8(revision_id)
 
2682
 
        return revno, revision_id
 
2684
 
    def _write_last_revision_info(self, revno, revision_id):
 
2685
 
        """Simply write out the revision id, with no checks.
 
2687
 
        Use set_last_revision_info to perform this safely.
 
2689
 
        Does not update the revision_history cache.
 
2690
 
        Intended to be called by set_last_revision_info and
 
2691
 
        _write_revision_history.
 
2693
 
        revision_id = _mod_revision.ensure_null(revision_id)
 
2694
 
        out_string = '%d %s\n' % (revno, revision_id)
 
2695
 
        self._transport.put_bytes('last-revision', out_string,
 
2696
 
            mode=self.bzrdir._get_file_mode())
 
2699
 
    def set_last_revision_info(self, revno, revision_id):
 
2700
 
        revision_id = _mod_revision.ensure_null(revision_id)
 
2701
 
        old_revno, old_revid = self.last_revision_info()
 
2702
 
        if self._get_append_revisions_only():
 
2703
 
            self._check_history_violation(revision_id)
 
2704
 
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
 
2705
 
        self._write_last_revision_info(revno, revision_id)
 
2706
 
        self._clear_cached_state()
 
2707
 
        self._last_revision_info_cache = revno, revision_id
 
2708
 
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2710
 
    def _synchronize_history(self, destination, revision_id):
 
2711
 
        """Synchronize last revision and revision history between branches.
 
2713
 
        :see: Branch._synchronize_history
 
2715
 
        # XXX: The base Branch has a fast implementation of this method based
 
2716
 
        # on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
 
2717
 
        # that uses set_revision_history.  This class inherits from BzrBranch5,
 
2718
 
        # but wants the fast implementation, so it calls
 
2719
 
        # Branch._synchronize_history directly.
 
2720
 
        Branch._synchronize_history(self, destination, revision_id)
 
2722
 
    def _check_history_violation(self, revision_id):
 
2723
 
        last_revision = _mod_revision.ensure_null(self.last_revision())
 
2724
 
        if _mod_revision.is_null(last_revision):
 
2726
 
        if last_revision not in self._lefthand_history(revision_id):
 
2727
 
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
 
2729
 
    def _gen_revision_history(self):
 
2730
 
        """Generate the revision history from last revision
 
2732
 
        last_revno, last_revision = self.last_revision_info()
 
2733
 
        self._extend_partial_history(stop_index=last_revno-1)
 
2734
 
        return list(reversed(self._partial_revision_history_cache))
 
2736
 
    def _write_revision_history(self, history):
 
2737
 
        """Factored out of set_revision_history.
 
2739
 
        This performs the actual writing to disk, with format-specific checks.
 
2740
 
        It is intended to be called by BzrBranch5.set_revision_history.
 
2742
 
        if len(history) == 0:
 
2743
 
            last_revision = 'null:'
 
2745
 
            if history != self._lefthand_history(history[-1]):
 
2746
 
                raise errors.NotLefthandHistory(history)
 
2747
 
            last_revision = history[-1]
 
2748
 
        if self._get_append_revisions_only():
 
2749
 
            self._check_history_violation(last_revision)
 
2750
 
        self._write_last_revision_info(len(history), last_revision)
 
2753
 
    def _set_parent_location(self, url):
 
2754
 
        """Set the parent branch"""
 
2755
 
        self._set_config_location('parent_location', url, make_relative=True)
 
2758
 
    def _get_parent_location(self):
 
2759
 
        """Set the parent branch"""
 
2760
 
        return self._get_config_location('parent_location')
 
2763
 
    def _set_all_reference_info(self, info_dict):
 
2764
 
        """Replace all reference info stored in a branch.
 
2766
 
        :param info_dict: A dict of {file_id: (tree_path, branch_location)}
 
2769
 
        writer = rio.RioWriter(s)
 
2770
 
        for key, (tree_path, branch_location) in info_dict.iteritems():
 
2771
 
            stanza = rio.Stanza(file_id=key, tree_path=tree_path,
 
2772
 
                                branch_location=branch_location)
 
2773
 
            writer.write_stanza(stanza)
 
2774
 
        self._transport.put_bytes('references', s.getvalue())
 
2775
 
        self._reference_info = info_dict
 
2778
 
    def _get_all_reference_info(self):
 
2779
 
        """Return all the reference info stored in a branch.
 
2781
 
        :return: A dict of {file_id: (tree_path, branch_location)}
 
2783
 
        if self._reference_info is not None:
 
2784
 
            return self._reference_info
 
2785
 
        rio_file = self._transport.get('references')
 
2787
 
            stanzas = rio.read_stanzas(rio_file)
 
2788
 
            info_dict = dict((s['file_id'], (s['tree_path'],
 
2789
 
                             s['branch_location'])) for s in stanzas)
 
2792
 
        self._reference_info = info_dict
 
2795
 
    def set_reference_info(self, file_id, tree_path, branch_location):
 
2796
 
        """Set the branch location to use for a tree reference.
 
2798
 
        :param file_id: The file-id of the tree reference.
 
2799
 
        :param tree_path: The path of the tree reference in the tree.
 
2800
 
        :param branch_location: The location of the branch to retrieve tree
 
2803
 
        info_dict = self._get_all_reference_info()
 
2804
 
        info_dict[file_id] = (tree_path, branch_location)
 
2805
 
        if None in (tree_path, branch_location):
 
2806
 
            if tree_path is not None:
 
2807
 
                raise ValueError('tree_path must be None when branch_location'
 
2809
 
            if branch_location is not None:
 
2810
 
                raise ValueError('branch_location must be None when tree_path'
 
2812
 
            del info_dict[file_id]
 
2813
 
        self._set_all_reference_info(info_dict)
 
2815
 
    def get_reference_info(self, file_id):
 
2816
 
        """Get the tree_path and branch_location for a tree reference.
 
2818
 
        :return: a tuple of (tree_path, branch_location)
 
2820
 
        return self._get_all_reference_info().get(file_id, (None, None))
 
2822
 
    def reference_parent(self, file_id, path, possible_transports=None):
 
2823
 
        """Return the parent branch for a tree-reference file_id.
 
2825
 
        :param file_id: The file_id of the tree reference
 
2826
 
        :param path: The path of the file_id in the tree
 
2827
 
        :return: A branch associated with the file_id
 
2829
 
        branch_location = self.get_reference_info(file_id)[1]
 
2830
 
        if branch_location is None:
 
2831
 
            return Branch.reference_parent(self, file_id, path,
 
2832
 
                                           possible_transports)
 
2833
 
        branch_location = urlutils.join(self.user_url, branch_location)
 
2834
 
        return Branch.open(branch_location,
 
2835
 
                           possible_transports=possible_transports)
 
2837
 
    def set_push_location(self, location):
 
2838
 
        """See Branch.set_push_location."""
 
2839
 
        self._set_config_location('push_location', location)
 
2841
 
    def set_bound_location(self, location):
 
2842
 
        """See Branch.set_push_location."""
 
2844
 
        config = self.get_config()
 
2845
 
        if location is None:
 
2846
 
            if config.get_user_option('bound') != 'True':
 
2849
 
                config.set_user_option('bound', 'False', warn_masked=True)
 
2852
 
            self._set_config_location('bound_location', location,
 
2854
 
            config.set_user_option('bound', 'True', warn_masked=True)
 
2857
 
    def _get_bound_location(self, bound):
 
2858
 
        """Return the bound location in the config file.
 
2860
 
        Return None if the bound parameter does not match"""
 
2861
 
        config = self.get_config()
 
2862
 
        config_bound = (config.get_user_option('bound') == 'True')
 
2863
 
        if config_bound != bound:
 
2865
 
        return self._get_config_location('bound_location', config=config)
 
2867
 
    def get_bound_location(self):
 
2868
 
        """See Branch.set_push_location."""
 
2869
 
        return self._get_bound_location(True)
 
2871
 
    def get_old_bound_location(self):
 
2872
 
        """See Branch.get_old_bound_location"""
 
2873
 
        return self._get_bound_location(False)
 
2875
 
    def get_stacked_on_url(self):
 
2876
 
        # you can always ask for the URL; but you might not be able to use it
 
2877
 
        # if the repo can't support stacking.
 
2878
 
        ## self._check_stackable_repo()
 
2879
 
        stacked_url = self._get_config_location('stacked_on_location')
 
2880
 
        if stacked_url is None:
 
2881
 
            raise errors.NotStacked(self)
 
2884
 
    def _get_append_revisions_only(self):
 
2885
 
        return self.get_config(
 
2886
 
            ).get_user_option_as_bool('append_revisions_only')
 
2889
 
    def generate_revision_history(self, revision_id, last_rev=None,
 
2891
 
        """See BzrBranch5.generate_revision_history"""
 
2892
 
        history = self._lefthand_history(revision_id, last_rev, other_branch)
 
2893
 
        revno = len(history)
 
2894
 
        self.set_last_revision_info(revno, revision_id)
 
2897
 
    def get_rev_id(self, revno, history=None):
 
2898
 
        """Find the revision id of the specified revno."""
 
2900
 
            return _mod_revision.NULL_REVISION
 
2902
 
        last_revno, last_revision_id = self.last_revision_info()
 
2903
 
        if revno <= 0 or revno > last_revno:
 
2904
 
            raise errors.NoSuchRevision(self, revno)
 
2906
 
        if history is not None:
 
2907
 
            return history[revno - 1]
 
2909
 
        index = last_revno - revno
 
2910
 
        if len(self._partial_revision_history_cache) <= index:
 
2911
 
            self._extend_partial_history(stop_index=index)
 
2912
 
        if len(self._partial_revision_history_cache) > index:
 
2913
 
            return self._partial_revision_history_cache[index]
 
2915
 
            raise errors.NoSuchRevision(self, revno)
 
2918
 
    def revision_id_to_revno(self, revision_id):
 
2919
 
        """Given a revision id, return its revno"""
 
2920
 
        if _mod_revision.is_null(revision_id):
 
2923
 
            index = self._partial_revision_history_cache.index(revision_id)
 
2925
 
            self._extend_partial_history(stop_revision=revision_id)
 
2926
 
            index = len(self._partial_revision_history_cache) - 1
 
2927
 
            if self._partial_revision_history_cache[index] != revision_id:
 
2928
 
                raise errors.NoSuchRevision(self, revision_id)
 
2929
 
        return self.revno() - index
 
2932
 
class BzrBranch7(BzrBranch8):
 
2933
 
    """A branch with support for a fallback repository."""
 
2935
 
    def set_reference_info(self, file_id, tree_path, branch_location):
 
2936
 
        Branch.set_reference_info(self, file_id, tree_path, branch_location)
 
2938
 
    def get_reference_info(self, file_id):
 
2939
 
        Branch.get_reference_info(self, file_id)
 
2941
 
    def reference_parent(self, file_id, path, possible_transports=None):
 
2942
 
        return Branch.reference_parent(self, file_id, path,
 
2943
 
                                       possible_transports)
 
2946
 
class BzrBranch6(BzrBranch7):
 
2947
 
    """See BzrBranchFormat6 for the capabilities of this branch.
 
2949
 
    This subclass of BzrBranch7 disables the new features BzrBranch7 added,
 
2953
 
    def get_stacked_on_url(self):
 
2954
 
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
 
 
1903
format_registry.register_lazy(
 
 
1904
    b"Bazaar-NG branch format 5\n", "breezy.bzr.fullhistory",
 
 
1906
format_registry.register_lazy(
 
 
1907
    b"Bazaar Branch Format 6 (bzr 0.15)\n",
 
 
1908
    "breezy.bzr.branch", "BzrBranchFormat6")
 
 
1909
format_registry.register_lazy(
 
 
1910
    b"Bazaar Branch Format 7 (needs bzr 1.6)\n",
 
 
1911
    "breezy.bzr.branch", "BzrBranchFormat7")
 
 
1912
format_registry.register_lazy(
 
 
1913
    b"Bazaar Branch Format 8 (needs bzr 1.15)\n",
 
 
1914
    "breezy.bzr.branch", "BzrBranchFormat8")
 
 
1915
format_registry.register_lazy(
 
 
1916
    b"Bazaar-NG Branch Reference Format 1\n",
 
 
1917
    "breezy.bzr.branch", "BranchReferenceFormat")
 
 
1919
format_registry.set_default_key(b"Bazaar Branch Format 7 (needs bzr 1.6)\n")
 
 
1922
class BranchWriteLockResult(LogicalLockResult):
 
 
1923
    """The result of write locking a branch.
 
 
1925
    :ivar token: The token obtained from the underlying branch lock, or
 
 
1927
    :ivar unlock: A callable which will unlock the lock.
 
 
1931
        return "BranchWriteLockResult(%r, %r)" % (self.unlock, self.token)
 
2957
1934
######################################################################
 
 
3225
2207
                if graph is None:
 
3226
2208
                    graph = self.target.repository.get_graph()
 
3227
2209
                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)])
 
 
2210
                    self.target.last_revision_info()
 
 
2211
                stop_revno = graph.find_distance_to_null(
 
 
2212
                    stop_revision, [(other_last_revision, other_revno),
 
 
2213
                                    (this_last_revision, this_revno)])
 
3232
2214
            self.target.set_last_revision_info(stop_revno, stop_revision)
 
3234
 
            self.source.unlock()
 
3236
2216
    def pull(self, overwrite=False, stop_revision=None,
 
3237
 
             possible_transports=None, _hook_master=None, run_hooks=True,
 
3238
 
             _override_hook_target=None, local=False):
 
 
2217
             possible_transports=None, run_hooks=True,
 
 
2218
             _override_hook_target=None, local=False,
 
 
2220
        """Pull from source into self, updating my master if any.
 
 
2222
        :param run_hooks: Private parameter - if false, this branch
 
 
2223
            is being called because it's the master of the primary branch,
 
 
2224
            so it should not run its hooks.
 
 
2226
        with cleanup.ExitStack() as exit_stack:
 
 
2227
            exit_stack.enter_context(self.target.lock_write())
 
 
2228
            bound_location = self.target.get_bound_location()
 
 
2229
            if local and not bound_location:
 
 
2230
                raise errors.LocalRequiresBoundBranch()
 
 
2231
            master_branch = None
 
 
2232
            source_is_master = False
 
 
2234
                # bound_location comes from a config file, some care has to be
 
 
2235
                # taken to relate it to source.user_url
 
 
2236
                normalized = urlutils.normalize_url(bound_location)
 
 
2238
                    relpath = self.source.user_transport.relpath(normalized)
 
 
2239
                    source_is_master = (relpath == '')
 
 
2240
                except (errors.PathNotChild, urlutils.InvalidURL):
 
 
2241
                    source_is_master = False
 
 
2242
            if not local and bound_location and not source_is_master:
 
 
2243
                # not pulling from master, so we need to update master.
 
 
2244
                master_branch = self.target.get_master_branch(
 
 
2245
                    possible_transports)
 
 
2246
                exit_stack.enter_context(master_branch.lock_write())
 
 
2248
                # pull from source into master.
 
 
2250
                    self.source, overwrite, stop_revision, run_hooks=False,
 
 
2251
                    tag_selector=tag_selector)
 
 
2253
                overwrite, stop_revision, _hook_master=master_branch,
 
 
2254
                run_hooks=run_hooks,
 
 
2255
                _override_hook_target=_override_hook_target,
 
 
2256
                merge_tags_to_master=not source_is_master,
 
 
2257
                tag_selector=tag_selector)
 
 
2259
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
 
2260
             _override_hook_source_branch=None, tag_selector=None):
 
 
2261
        """See InterBranch.push.
 
 
2263
        This is the basic concrete implementation of push()
 
 
2265
        :param _override_hook_source_branch: If specified, run the hooks
 
 
2266
            passing this Branch as the source, rather than self.  This is for
 
 
2267
            use of RemoteBranch, where push is delegated to the underlying
 
 
2271
            raise errors.LossyPushToSameVCS(self.source, self.target)
 
 
2272
        # TODO: Public option to disable running hooks - should be trivial but
 
 
2276
            if _override_hook_source_branch:
 
 
2277
                result.source_branch = _override_hook_source_branch
 
 
2278
            for hook in Branch.hooks['post_push']:
 
 
2281
        with self.source.lock_read(), self.target.lock_write():
 
 
2282
            bound_location = self.target.get_bound_location()
 
 
2283
            if bound_location and self.target.base != bound_location:
 
 
2284
                # there is a master branch.
 
 
2286
                # XXX: Why the second check?  Is it even supported for a branch
 
 
2287
                # to be bound to itself? -- mbp 20070507
 
 
2288
                master_branch = self.target.get_master_branch()
 
 
2289
                with master_branch.lock_write():
 
 
2290
                    # push into the master from the source branch.
 
 
2291
                    master_inter = InterBranch.get(self.source, master_branch)
 
 
2292
                    master_inter._basic_push(
 
 
2293
                        overwrite, stop_revision, tag_selector=tag_selector)
 
 
2294
                    # and push into the target branch from the source. Note
 
 
2295
                    # that we push from the source branch again, because it's
 
 
2296
                    # considered the highest bandwidth repository.
 
 
2297
                    result = self._basic_push(
 
 
2298
                        overwrite, stop_revision, tag_selector=tag_selector)
 
 
2299
                    result.master_branch = master_branch
 
 
2300
                    result.local_branch = self.target
 
 
2303
                master_branch = None
 
 
2305
                result = self._basic_push(
 
 
2306
                    overwrite, stop_revision, tag_selector=tag_selector)
 
 
2307
                # TODO: Why set master_branch and local_branch if there's no
 
 
2308
                # binding?  Maybe cleaner to just leave them unset? -- mbp
 
 
2310
                result.master_branch = self.target
 
 
2311
                result.local_branch = None
 
 
2315
    def _basic_push(self, overwrite, stop_revision, tag_selector=None):
 
 
2316
        """Basic implementation of push without bound branches or hooks.
 
 
2318
        Must be called with source read locked and target write locked.
 
 
2320
        result = BranchPushResult()
 
 
2321
        result.source_branch = self.source
 
 
2322
        result.target_branch = self.target
 
 
2323
        result.old_revno, result.old_revid = self.target.last_revision_info()
 
 
2324
        overwrite = _fix_overwrite_type(overwrite)
 
 
2325
        if result.old_revid != stop_revision:
 
 
2326
            # We assume that during 'push' this repository is closer than
 
 
2328
            graph = self.source.repository.get_graph(self.target.repository)
 
 
2329
            self._update_revisions(
 
 
2330
                stop_revision, overwrite=("history" in overwrite), graph=graph)
 
 
2331
        if self.source._push_should_merge_tags():
 
 
2332
            result.tag_updates, result.tag_conflicts = (
 
 
2333
                self.source.tags.merge_to(
 
 
2334
                    self.target.tags, "tags" in overwrite, selector=tag_selector))
 
 
2335
        self.update_references()
 
 
2336
        result.new_revno, result.new_revid = self.target.last_revision_info()
 
 
2339
    def _pull(self, overwrite=False, stop_revision=None,
 
 
2340
              possible_transports=None, _hook_master=None, run_hooks=True,
 
 
2341
              _override_hook_target=None, local=False,
 
 
2342
              merge_tags_to_master=True, tag_selector=None):
 
3239
2343
        """See Branch.pull.
 
 
2345
        This function is the core worker, used by GenericInterBranch.pull to
 
 
2346
        avoid duplication when pulling source->master and source->local.
 
3241
2348
        :param _hook_master: Private parameter - set the branch to
 
3242
2349
            be supplied as the master to pull hooks.
 
3243
2350
        :param run_hooks: Private parameter - if false, this branch
 
3244
2351
            is being called because it's the master of the primary branch,
 
3245
2352
            so it should not run its hooks.
 
 
2353
            is being called because it's the master of the primary branch,
 
 
2354
            so it should not run its hooks.
 
3246
2355
        :param _override_hook_target: Private parameter - set the branch to be
 
3247
2356
            supplied as the target_branch to pull hooks.
 
3248
2357
        :param local: Only update the local branch, and not the bound branch.