~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Jelmer Vernooij
  • Date: 2011-02-19 15:23:08 UTC
  • mto: (5582.12.2 weave-plugin)
  • mto: This revision was merged to the branch mainline in revision 5718.
  • Revision ID: jelmer@samba.org-20110219152308-5shhc4rj0ez4oa12
move xml4 to weave plugin.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2012 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
from __future__ import absolute_import
18
 
 
19
 
import bzrlib.bzrdir
20
17
 
21
18
from cStringIO import StringIO
 
19
import sys
22
20
 
23
21
from bzrlib.lazy_import import lazy_import
24
22
lazy_import(globals(), """
25
 
import itertools
 
23
from itertools import chain
26
24
from bzrlib import (
27
 
    bzrdir,
28
 
    controldir,
29
 
    cache_utf8,
30
 
    cleanup,
31
 
    config as _mod_config,
32
 
    debug,
33
 
    errors,
34
 
    fetch,
35
 
    graph as _mod_graph,
36
 
    lockdir,
37
 
    lockable_files,
38
 
    remote,
39
 
    repository,
40
 
    revision as _mod_revision,
41
 
    rio,
42
 
    tag as _mod_tag,
43
 
    transport,
44
 
    ui,
45
 
    urlutils,
46
 
    vf_search,
 
25
        bzrdir,
 
26
        cache_utf8,
 
27
        config as _mod_config,
 
28
        controldir,
 
29
        debug,
 
30
        errors,
 
31
        fetch,
 
32
        graph as _mod_graph,
 
33
        lockdir,
 
34
        lockable_files,
 
35
        remote,
 
36
        repository,
 
37
        revision as _mod_revision,
 
38
        rio,
 
39
        symbol_versioning,
 
40
        transport,
 
41
        tsort,
 
42
        ui,
 
43
        urlutils,
 
44
        )
 
45
from bzrlib.config import BranchConfig, TransportConfig
 
46
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
 
47
from bzrlib.tag import (
 
48
    BasicTags,
 
49
    DisabledTags,
47
50
    )
48
 
from bzrlib.i18n import gettext, ngettext
49
51
""")
50
52
 
51
 
# Explicitly import bzrlib.bzrdir so that the BzrProber
52
 
# is guaranteed to be registered.
53
 
import bzrlib.bzrdir
54
 
 
55
 
from bzrlib import (
56
 
    bzrdir,
57
 
    controldir,
58
 
    )
59
 
from bzrlib.decorators import (
60
 
    needs_read_lock,
61
 
    needs_write_lock,
62
 
    only_raises,
63
 
    )
64
 
from bzrlib.hooks import Hooks
 
53
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
 
54
from bzrlib.hooks import HookPoint, Hooks
65
55
from bzrlib.inter import InterObject
66
56
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
67
57
from bzrlib import registry
72
62
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
73
63
 
74
64
 
 
65
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
 
66
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
 
67
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
 
68
 
 
69
 
75
70
class Branch(controldir.ControlComponent):
76
71
    """Branch holding a history of revisions.
77
72
 
78
73
    :ivar base:
79
74
        Base directory/url of the branch; using control_url and
80
75
        control_transport is more standardized.
81
 
    :ivar hooks: An instance of BranchHooks.
82
 
    :ivar _master_branch_cache: cached result of get_master_branch, see
83
 
        _clear_cached_state.
 
76
 
 
77
    hooks: An instance of BranchHooks.
84
78
    """
85
79
    # this is really an instance variable - FIXME move it there
86
80
    # - RBC 20060112
94
88
    def user_transport(self):
95
89
        return self.bzrdir.user_transport
96
90
 
97
 
    def __init__(self, possible_transports=None):
 
91
    def __init__(self, *ignored, **ignored_too):
98
92
        self.tags = self._format.make_tags(self)
99
93
        self._revision_history_cache = None
100
94
        self._revision_id_to_revno_cache = None
102
96
        self._partial_revision_history_cache = []
103
97
        self._tags_bytes = None
104
98
        self._last_revision_info_cache = None
105
 
        self._master_branch_cache = None
106
99
        self._merge_sorted_revisions_cache = None
107
 
        self._open_hook(possible_transports)
 
100
        self._open_hook()
108
101
        hooks = Branch.hooks['open']
109
102
        for hook in hooks:
110
103
            hook(self)
111
104
 
112
 
    def _open_hook(self, possible_transports):
 
105
    def _open_hook(self):
113
106
        """Called by init to allow simpler extension of the base class."""
114
107
 
115
 
    def _activate_fallback_location(self, url, possible_transports):
 
108
    def _activate_fallback_location(self, url):
116
109
        """Activate the branch/repository from url as a fallback repository."""
117
110
        for existing_fallback_repo in self.repository._fallback_repositories:
118
111
            if existing_fallback_repo.user_url == url:
119
112
                # This fallback is already configured.  This probably only
120
 
                # happens because ControlDir.sprout is a horrible mess.  To avoid
 
113
                # happens because BzrDir.sprout is a horrible mess.  To avoid
121
114
                # confusing _unstack we don't add this a second time.
122
115
                mutter('duplicate activation of fallback %r on %r', url, self)
123
116
                return
124
 
        repo = self._get_fallback_repository(url, possible_transports)
 
117
        repo = self._get_fallback_repository(url)
125
118
        if repo.has_same_location(self.repository):
126
119
            raise errors.UnstackableLocationError(self.user_url, url)
127
120
        self.repository.add_fallback_repository(repo)
181
174
        For instance, if the branch is at URL/.bzr/branch,
182
175
        Branch.open(URL) -> a Branch instance.
183
176
        """
184
 
        control = controldir.ControlDir.open(base,
185
 
            possible_transports=possible_transports, _unsupported=_unsupported)
186
 
        return control.open_branch(unsupported=_unsupported,
187
 
            possible_transports=possible_transports)
 
177
        control = bzrdir.BzrDir.open(base, _unsupported,
 
178
                                     possible_transports=possible_transports)
 
179
        return control.open_branch(unsupported=_unsupported)
188
180
 
189
181
    @staticmethod
190
 
    def open_from_transport(transport, name=None, _unsupported=False,
191
 
            possible_transports=None):
 
182
    def open_from_transport(transport, name=None, _unsupported=False):
192
183
        """Open the branch rooted at transport"""
193
 
        control = controldir.ControlDir.open_from_transport(transport, _unsupported)
194
 
        return control.open_branch(name=name, unsupported=_unsupported,
195
 
            possible_transports=possible_transports)
 
184
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
 
185
        return control.open_branch(name=name, unsupported=_unsupported)
196
186
 
197
187
    @staticmethod
198
188
    def open_containing(url, possible_transports=None):
206
196
        format, UnknownFormatError or UnsupportedFormatError are raised.
207
197
        If there is one, it is returned, along with the unused portion of url.
208
198
        """
209
 
        control, relpath = controldir.ControlDir.open_containing(url,
 
199
        control, relpath = bzrdir.BzrDir.open_containing(url,
210
200
                                                         possible_transports)
211
 
        branch = control.open_branch(possible_transports=possible_transports)
212
 
        return (branch, relpath)
 
201
        return control.open_branch(), relpath
213
202
 
214
203
    def _push_should_merge_tags(self):
215
204
        """Should _basic_push merge this branch's tags into the target?
227
216
 
228
217
        :return: A bzrlib.config.BranchConfig.
229
218
        """
230
 
        return _mod_config.BranchConfig(self)
231
 
 
232
 
    def get_config_stack(self):
233
 
        """Get a bzrlib.config.BranchStack for this Branch.
234
 
 
235
 
        This can then be used to get and set configuration options for the
236
 
        branch.
237
 
 
238
 
        :return: A bzrlib.config.BranchStack.
239
 
        """
240
 
        return _mod_config.BranchStack(self)
 
219
        return BranchConfig(self)
241
220
 
242
221
    def _get_config(self):
243
222
        """Get the concrete config for just the config in this branch.
251
230
        """
252
231
        raise NotImplementedError(self._get_config)
253
232
 
254
 
    def _get_fallback_repository(self, url, possible_transports):
 
233
    def _get_fallback_repository(self, url):
255
234
        """Get the repository we fallback to at url."""
256
235
        url = urlutils.join(self.base, url)
257
 
        a_branch = Branch.open(url, possible_transports=possible_transports)
 
236
        a_branch = Branch.open(url,
 
237
            possible_transports=[self.bzrdir.root_transport])
258
238
        return a_branch.repository
259
239
 
260
240
    @needs_read_lock
469
449
            after. If None, the rest of history is included.
470
450
        :param stop_rule: if stop_revision_id is not None, the precise rule
471
451
            to use for termination:
472
 
 
473
452
            * 'exclude' - leave the stop revision out of the result (default)
474
453
            * 'include' - the stop revision is the last item in the result
475
454
            * 'with-merges' - include the stop revision and all of its
477
456
            * 'with-merges-without-common-ancestry' - filter out revisions 
478
457
              that are in both ancestries
479
458
        :param direction: either 'reverse' or 'forward':
480
 
 
481
459
            * reverse means return the start_revision_id first, i.e.
482
460
              start at the most recent revision and go backwards in history
483
461
            * forward returns tuples in the opposite order to reverse.
527
505
        rev_iter = iter(merge_sorted_revisions)
528
506
        if start_revision_id is not None:
529
507
            for node in rev_iter:
530
 
                rev_id = node.key
 
508
                rev_id = node.key[-1]
531
509
                if rev_id != start_revision_id:
532
510
                    continue
533
511
                else:
534
512
                    # The decision to include the start or not
535
513
                    # depends on the stop_rule if a stop is provided
536
514
                    # so pop this node back into the iterator
537
 
                    rev_iter = itertools.chain(iter([node]), rev_iter)
 
515
                    rev_iter = chain(iter([node]), rev_iter)
538
516
                    break
539
517
        if stop_revision_id is None:
540
518
            # Yield everything
541
519
            for node in rev_iter:
542
 
                rev_id = node.key
 
520
                rev_id = node.key[-1]
543
521
                yield (rev_id, node.merge_depth, node.revno,
544
522
                       node.end_of_merge)
545
523
        elif stop_rule == 'exclude':
546
524
            for node in rev_iter:
547
 
                rev_id = node.key
 
525
                rev_id = node.key[-1]
548
526
                if rev_id == stop_revision_id:
549
527
                    return
550
528
                yield (rev_id, node.merge_depth, node.revno,
551
529
                       node.end_of_merge)
552
530
        elif stop_rule == 'include':
553
531
            for node in rev_iter:
554
 
                rev_id = node.key
 
532
                rev_id = node.key[-1]
555
533
                yield (rev_id, node.merge_depth, node.revno,
556
534
                       node.end_of_merge)
557
535
                if rev_id == stop_revision_id:
563
541
            ancestors = graph.find_unique_ancestors(start_revision_id,
564
542
                                                    [stop_revision_id])
565
543
            for node in rev_iter:
566
 
                rev_id = node.key
 
544
                rev_id = node.key[-1]
567
545
                if rev_id not in ancestors:
568
546
                    continue
569
547
                yield (rev_id, node.merge_depth, node.revno,
579
557
            reached_stop_revision_id = False
580
558
            revision_id_whitelist = []
581
559
            for node in rev_iter:
582
 
                rev_id = node.key
 
560
                rev_id = node.key[-1]
583
561
                if rev_id == left_parent:
584
562
                    # reached the left parent after the stop_revision
585
563
                    return
665
643
        """
666
644
        raise errors.UpgradeRequired(self.user_url)
667
645
 
668
 
    def get_append_revisions_only(self):
669
 
        """Whether it is only possible to append revisions to the history.
670
 
        """
671
 
        if not self._format.supports_set_append_revisions_only():
672
 
            return False
673
 
        return self.get_config_stack().get('append_revisions_only')
674
 
 
675
646
    def set_append_revisions_only(self, enabled):
676
647
        if not self._format.supports_set_append_revisions_only():
677
648
            raise errors.UpgradeRequired(self.user_url)
678
 
        self.get_config_stack().set('append_revisions_only', enabled)
 
649
        if enabled:
 
650
            value = 'True'
 
651
        else:
 
652
            value = 'False'
 
653
        self.get_config().set_user_option('append_revisions_only', value,
 
654
            warn_masked=True)
679
655
 
680
656
    def set_reference_info(self, file_id, tree_path, branch_location):
681
657
        """Set the branch location to use for a tree reference."""
686
662
        raise errors.UnsupportedOperation(self.get_reference_info, self)
687
663
 
688
664
    @needs_write_lock
689
 
    def fetch(self, from_branch, last_revision=None, limit=None):
 
665
    def fetch(self, from_branch, last_revision=None, pb=None, fetch_spec=None):
690
666
        """Copy revisions from from_branch into this branch.
691
667
 
692
668
        :param from_branch: Where to copy from.
693
669
        :param last_revision: What revision to stop at (None for at the end
694
670
                              of the branch.
695
 
        :param limit: Optional rough limit of revisions to fetch
 
671
        :param pb: An optional progress bar to use.
 
672
        :param fetch_spec: If specified, a SearchResult or
 
673
            PendingAncestryResult that describes which revisions to copy.  This
 
674
            allows copying multiple heads at once.  Mutually exclusive with
 
675
            last_revision.
696
676
        :return: None
697
677
        """
698
 
        return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
 
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
        if self.base == from_branch.base:
 
682
            return (0, [])
 
683
        if pb is not None:
 
684
            symbol_versioning.warn(
 
685
                symbol_versioning.deprecated_in((1, 14, 0))
 
686
                % "pb parameter to fetch()")
 
687
        from_branch.lock_read()
 
688
        try:
 
689
            if last_revision is None and fetch_spec is None:
 
690
                last_revision = from_branch.last_revision()
 
691
                last_revision = _mod_revision.ensure_null(last_revision)
 
692
            return self.repository.fetch(from_branch.repository,
 
693
                                         revision_id=last_revision,
 
694
                                         pb=pb, fetch_spec=fetch_spec)
 
695
        finally:
 
696
            from_branch.unlock()
699
697
 
700
698
    def get_bound_location(self):
701
699
        """Return the URL of the branch we are bound to.
710
708
        """
711
709
        raise errors.UpgradeRequired(self.user_url)
712
710
 
713
 
    def get_commit_builder(self, parents, config_stack=None, timestamp=None,
 
711
    def get_commit_builder(self, parents, config=None, timestamp=None,
714
712
                           timezone=None, committer=None, revprops=None,
715
 
                           revision_id=None, lossy=False):
 
713
                           revision_id=None):
716
714
        """Obtain a CommitBuilder for this branch.
717
715
 
718
716
        :param parents: Revision ids of the parents of the new revision.
722
720
        :param committer: Optional committer to set for commit.
723
721
        :param revprops: Optional dictionary of revision properties.
724
722
        :param revision_id: Optional revision id.
725
 
        :param lossy: Whether to discard data that can not be natively
726
 
            represented, when pushing to a foreign VCS 
727
723
        """
728
724
 
729
 
        if config_stack is None:
730
 
            config_stack = self.get_config_stack()
 
725
        if config is None:
 
726
            config = self.get_config()
731
727
 
732
 
        return self.repository.get_commit_builder(self, parents, config_stack,
733
 
            timestamp, timezone, committer, revprops, revision_id,
734
 
            lossy)
 
728
        return self.repository.get_commit_builder(self, parents, config,
 
729
            timestamp, timezone, committer, revprops, revision_id)
735
730
 
736
731
    def get_master_branch(self, possible_transports=None):
737
732
        """Return the branch we are bound to.
740
735
        """
741
736
        return None
742
737
 
743
 
    @deprecated_method(deprecated_in((2, 5, 0)))
744
738
    def get_revision_delta(self, revno):
745
739
        """Return the delta for one revision.
746
740
 
747
741
        The delta is relative to its mainline predecessor, or the
748
742
        empty tree for revision 1.
749
743
        """
750
 
        try:
751
 
            revid = self.get_rev_id(revno)
752
 
        except errors.NoSuchRevision:
 
744
        rh = self.revision_history()
 
745
        if not (1 <= revno <= len(rh)):
753
746
            raise errors.InvalidRevisionNumber(revno)
754
 
        return self.repository.get_revision_delta(revid)
 
747
        return self.repository.get_revision_delta(rh[revno-1])
755
748
 
756
749
    def get_stacked_on_url(self):
757
750
        """Get the URL this branch is stacked against.
766
759
        """Print `file` to stdout."""
767
760
        raise NotImplementedError(self.print_file)
768
761
 
769
 
    @deprecated_method(deprecated_in((2, 4, 0)))
770
762
    def set_revision_history(self, rev_history):
771
 
        """See Branch.set_revision_history."""
772
 
        self._set_revision_history(rev_history)
773
 
 
774
 
    @needs_write_lock
775
 
    def _set_revision_history(self, rev_history):
776
 
        if len(rev_history) == 0:
777
 
            revid = _mod_revision.NULL_REVISION
778
 
        else:
779
 
            revid = rev_history[-1]
780
 
        if rev_history != self._lefthand_history(revid):
781
 
            raise errors.NotLefthandHistory(rev_history)
782
 
        self.set_last_revision_info(len(rev_history), revid)
783
 
        self._cache_revision_history(rev_history)
784
 
        for hook in Branch.hooks['set_rh']:
785
 
            hook(self, rev_history)
786
 
 
787
 
    @needs_write_lock
788
 
    def set_last_revision_info(self, revno, revision_id):
789
 
        """Set the last revision of this branch.
790
 
 
791
 
        The caller is responsible for checking that the revno is correct
792
 
        for this revision id.
793
 
 
794
 
        It may be possible to set the branch last revision to an id not
795
 
        present in the repository.  However, branches can also be
796
 
        configured to check constraints on history, in which case this may not
797
 
        be permitted.
798
 
        """
799
 
        raise NotImplementedError(self.set_last_revision_info)
800
 
 
801
 
    @needs_write_lock
802
 
    def generate_revision_history(self, revision_id, last_rev=None,
803
 
                                  other_branch=None):
804
 
        """See Branch.generate_revision_history"""
805
 
        graph = self.repository.get_graph()
806
 
        (last_revno, last_revid) = self.last_revision_info()
807
 
        known_revision_ids = [
808
 
            (last_revid, last_revno),
809
 
            (_mod_revision.NULL_REVISION, 0),
810
 
            ]
811
 
        if last_rev is not None:
812
 
            if not graph.is_ancestor(last_rev, revision_id):
813
 
                # our previous tip is not merged into stop_revision
814
 
                raise errors.DivergedBranches(self, other_branch)
815
 
        revno = graph.find_distance_to_null(revision_id, known_revision_ids)
816
 
        self.set_last_revision_info(revno, revision_id)
 
763
        raise NotImplementedError(self.set_revision_history)
817
764
 
818
765
    @needs_write_lock
819
766
    def set_parent(self, url):
856
803
                return
857
804
            self._unstack()
858
805
        else:
859
 
            self._activate_fallback_location(url,
860
 
                possible_transports=[self.bzrdir.root_transport])
 
806
            self._activate_fallback_location(url)
861
807
        # write this out after the repository is stacked to avoid setting a
862
808
        # stacked config that doesn't work.
863
809
        self._set_config_location('stacked_on_location', url)
864
810
 
865
811
    def _unstack(self):
866
812
        """Change a branch to be unstacked, copying data as needed.
867
 
 
 
813
        
868
814
        Don't call this directly, use set_stacked_on_url(None).
869
815
        """
870
816
        pb = ui.ui_factory.nested_progress_bar()
871
817
        try:
872
 
            pb.update(gettext("Unstacking"))
 
818
            pb.update("Unstacking")
873
819
            # The basic approach here is to fetch the tip of the branch,
874
820
            # including all available ghosts, from the existing stacked
875
821
            # repository into a new repository object without the fallbacks. 
889
835
            # stream from one of them to the other.  This does mean doing
890
836
            # separate SSH connection setup, but unstacking is not a
891
837
            # common operation so it's tolerable.
892
 
            new_bzrdir = controldir.ControlDir.open(
893
 
                self.bzrdir.root_transport.base)
 
838
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
894
839
            new_repository = new_bzrdir.find_repository()
895
840
            if new_repository._fallback_repositories:
896
841
                raise AssertionError("didn't expect %r to have "
939
884
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
940
885
                except errors.TagsNotSupported:
941
886
                    tags_to_fetch = set()
942
 
                fetch_spec = vf_search.NotInOtherForRevs(self.repository,
 
887
                fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
943
888
                    old_repository, required_ids=[self.last_revision()],
944
889
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
945
890
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
953
898
 
954
899
        :seealso: Branch._get_tags_bytes.
955
900
        """
956
 
        op = cleanup.OperationWithCleanups(self._set_tags_bytes_locked)
957
 
        op.add_cleanup(self.lock_write().unlock)
958
 
        return op.run_simple(bytes)
 
901
        return _run_with_write_locked_target(self, self._set_tags_bytes_locked,
 
902
                bytes)
959
903
 
960
904
    def _set_tags_bytes_locked(self, bytes):
961
905
        self._tags_bytes = bytes
992
936
        self._revision_history_cache = None
993
937
        self._revision_id_to_revno_cache = None
994
938
        self._last_revision_info_cache = None
995
 
        self._master_branch_cache = None
996
939
        self._merge_sorted_revisions_cache = None
997
940
        self._partial_revision_history_cache = []
998
941
        self._partial_revision_id_to_revno_cache = {}
1013
956
        """
1014
957
        raise NotImplementedError(self._gen_revision_history)
1015
958
 
1016
 
    @deprecated_method(deprecated_in((2, 5, 0)))
1017
959
    @needs_read_lock
1018
960
    def revision_history(self):
1019
961
        """Return sequence of revision ids on this branch.
1021
963
        This method will cache the revision history for as long as it is safe to
1022
964
        do so.
1023
965
        """
1024
 
        return self._revision_history()
1025
 
 
1026
 
    def _revision_history(self):
1027
966
        if 'evil' in debug.debug_flags:
1028
967
            mutter_callsite(3, "revision_history scales with history.")
1029
968
        if self._revision_history_cache is not None:
1056
995
        :return: A tuple (revno, revision_id).
1057
996
        """
1058
997
        if self._last_revision_info_cache is None:
1059
 
            self._last_revision_info_cache = self._read_last_revision_info()
 
998
            self._last_revision_info_cache = self._last_revision_info()
1060
999
        return self._last_revision_info_cache
1061
1000
 
1062
 
    def _read_last_revision_info(self):
1063
 
        raise NotImplementedError(self._read_last_revision_info)
 
1001
    def _last_revision_info(self):
 
1002
        rh = self.revision_history()
 
1003
        revno = len(rh)
 
1004
        if revno:
 
1005
            return (revno, rh[-1])
 
1006
        else:
 
1007
            return (0, _mod_revision.NULL_REVISION)
 
1008
 
 
1009
    @deprecated_method(deprecated_in((1, 6, 0)))
 
1010
    def missing_revisions(self, other, stop_revision=None):
 
1011
        """Return a list of new revisions that would perfectly fit.
 
1012
 
 
1013
        If self and other have not diverged, return a list of the revisions
 
1014
        present in other, but missing from self.
 
1015
        """
 
1016
        self_history = self.revision_history()
 
1017
        self_len = len(self_history)
 
1018
        other_history = other.revision_history()
 
1019
        other_len = len(other_history)
 
1020
        common_index = min(self_len, other_len) -1
 
1021
        if common_index >= 0 and \
 
1022
            self_history[common_index] != other_history[common_index]:
 
1023
            raise errors.DivergedBranches(self, other)
 
1024
 
 
1025
        if stop_revision is None:
 
1026
            stop_revision = other_len
 
1027
        else:
 
1028
            if stop_revision > other_len:
 
1029
                raise errors.NoSuchRevision(self, stop_revision)
 
1030
        return other_history[self_len:stop_revision]
 
1031
 
 
1032
    def update_revisions(self, other, stop_revision=None, overwrite=False,
 
1033
                         graph=None, fetch_tags=True):
 
1034
        """Pull in new perfect-fit revisions.
 
1035
 
 
1036
        :param other: Another Branch to pull from
 
1037
        :param stop_revision: Updated until the given revision
 
1038
        :param overwrite: Always set the branch pointer, rather than checking
 
1039
            to see if it is a proper descendant.
 
1040
        :param graph: A Graph object that can be used to query history
 
1041
            information. This can be None.
 
1042
        :param fetch_tags: Flag that specifies if tags from other should be
 
1043
            fetched too.
 
1044
        :return: None
 
1045
        """
 
1046
        return InterBranch.get(other, self).update_revisions(stop_revision,
 
1047
            overwrite, graph, fetch_tags=fetch_tags)
1064
1048
 
1065
1049
    @deprecated_method(deprecated_in((2, 4, 0)))
1066
1050
    def import_last_revision_info(self, source_repo, revno, revid):
1074
1058
            self.repository.fetch(source_repo, revision_id=revid)
1075
1059
        self.set_last_revision_info(revno, revid)
1076
1060
 
1077
 
    def import_last_revision_info_and_tags(self, source, revno, revid,
1078
 
                                           lossy=False):
 
1061
    def import_last_revision_info_and_tags(self, source, revno, revid):
1079
1062
        """Set the last revision info, importing from another repo if necessary.
1080
1063
 
1081
1064
        This is used by the bound branch code to upload a revision to
1085
1068
        :param source: Source branch to optionally fetch from
1086
1069
        :param revno: Revision number of the new tip
1087
1070
        :param revid: Revision id of the new tip
1088
 
        :param lossy: Whether to discard metadata that can not be
1089
 
            natively represented
1090
 
        :return: Tuple with the new revision number and revision id
1091
 
            (should only be different from the arguments when lossy=True)
1092
1071
        """
1093
1072
        if not self.repository.has_same_location(source.repository):
1094
 
            self.fetch(source, revid)
 
1073
            try:
 
1074
                tags_to_fetch = set(source.tags.get_reverse_tag_dict())
 
1075
            except errors.TagsNotSupported:
 
1076
                tags_to_fetch = set()
 
1077
            fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
 
1078
                source.repository, [revid],
 
1079
                if_present_ids=tags_to_fetch).execute()
 
1080
            self.repository.fetch(source.repository, fetch_spec=fetch_spec)
1095
1081
        self.set_last_revision_info(revno, revid)
1096
 
        return (revno, revid)
1097
1082
 
1098
1083
    def revision_id_to_revno(self, revision_id):
1099
1084
        """Given a revision id, return its revno"""
1100
1085
        if _mod_revision.is_null(revision_id):
1101
1086
            return 0
1102
 
        history = self._revision_history()
 
1087
        history = self.revision_history()
1103
1088
        try:
1104
1089
            return history.index(revision_id) + 1
1105
1090
        except ValueError:
1132
1117
            stop_revision=stop_revision,
1133
1118
            possible_transports=possible_transports, *args, **kwargs)
1134
1119
 
1135
 
    def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1136
 
            *args, **kwargs):
 
1120
    def push(self, target, overwrite=False, stop_revision=None, *args,
 
1121
        **kwargs):
1137
1122
        """Mirror this branch into target.
1138
1123
 
1139
1124
        This branch is considered to be 'local', having low latency.
1140
1125
        """
1141
1126
        return InterBranch.get(self, target).push(overwrite, stop_revision,
1142
 
            lossy, *args, **kwargs)
 
1127
            *args, **kwargs)
 
1128
 
 
1129
    def lossy_push(self, target, stop_revision=None):
 
1130
        """Push deltas into another branch.
 
1131
 
 
1132
        :note: This does not, like push, retain the revision ids from 
 
1133
            the source branch and will, rather than adding bzr-specific 
 
1134
            metadata, push only those semantics of the revision that can be 
 
1135
            natively represented by this branch' VCS.
 
1136
 
 
1137
        :param target: Target branch
 
1138
        :param stop_revision: Revision to push, defaults to last revision.
 
1139
        :return: BranchPushResult with an extra member revidmap: 
 
1140
            A dictionary mapping revision ids from the target branch 
 
1141
            to new revision ids in the target branch, for each 
 
1142
            revision that was pushed.
 
1143
        """
 
1144
        inter = InterBranch.get(self, target)
 
1145
        lossy_push = getattr(inter, "lossy_push", None)
 
1146
        if lossy_push is None:
 
1147
            raise errors.LossyPushToSameVCS(self, target)
 
1148
        return lossy_push(stop_revision)
1143
1149
 
1144
1150
    def basis_tree(self):
1145
1151
        """Return `Tree` object for last revision."""
1170
1176
    def _set_config_location(self, name, url, config=None,
1171
1177
                             make_relative=False):
1172
1178
        if config is None:
1173
 
            config = self.get_config_stack()
 
1179
            config = self.get_config()
1174
1180
        if url is None:
1175
1181
            url = ''
1176
1182
        elif make_relative:
1177
1183
            url = urlutils.relative_url(self.base, url)
1178
 
        config.set(name, url)
 
1184
        config.set_user_option(name, url, warn_masked=True)
1179
1185
 
1180
1186
    def _get_config_location(self, name, config=None):
1181
1187
        if config is None:
1182
 
            config = self.get_config_stack()
1183
 
        location = config.get(name)
 
1188
            config = self.get_config()
 
1189
        location = config.get_user_option(name)
1184
1190
        if location == '':
1185
1191
            location = None
1186
1192
        return location
1187
1193
 
1188
1194
    def get_child_submit_format(self):
1189
1195
        """Return the preferred format of submissions to this branch."""
1190
 
        return self.get_config_stack().get('child_submit_format')
 
1196
        return self.get_config().get_user_option("child_submit_format")
1191
1197
 
1192
1198
    def get_submit_branch(self):
1193
1199
        """Return the submit location of the branch.
1196
1202
        pattern is that the user can override it by specifying a
1197
1203
        location.
1198
1204
        """
1199
 
        return self.get_config_stack().get('submit_branch')
 
1205
        return self.get_config().get_user_option('submit_branch')
1200
1206
 
1201
1207
    def set_submit_branch(self, location):
1202
1208
        """Return the submit location of the branch.
1205
1211
        pattern is that the user can override it by specifying a
1206
1212
        location.
1207
1213
        """
1208
 
        self.get_config_stack().set('submit_branch', location)
 
1214
        self.get_config().set_user_option('submit_branch', location,
 
1215
            warn_masked=True)
1209
1216
 
1210
1217
    def get_public_branch(self):
1211
1218
        """Return the public location of the branch.
1224
1231
        self._set_config_location('public_branch', location)
1225
1232
 
1226
1233
    def get_push_location(self):
1227
 
        """Return None or the location to push this branch to."""
1228
 
        return self.get_config_stack().get('push_location')
 
1234
        """Return the None or the location to push this branch to."""
 
1235
        push_loc = self.get_config().get_user_option('push_location')
 
1236
        return push_loc
1229
1237
 
1230
1238
    def set_push_location(self, location):
1231
1239
        """Set a new push location for this branch."""
1316
1324
            if repository_policy is not None:
1317
1325
                repository_policy.configure_branch(result)
1318
1326
            self.copy_content_into(result, revision_id=revision_id)
1319
 
            master_url = self.get_bound_location()
1320
 
            if master_url is None:
1321
 
                result.set_parent(self.bzrdir.root_transport.base)
1322
 
            else:
1323
 
                result.set_parent(master_url)
 
1327
            result.set_parent(self.bzrdir.root_transport.base)
1324
1328
        finally:
1325
1329
            result.unlock()
1326
1330
        return result
1400
1404
        # TODO: We should probably also check that self.revision_history
1401
1405
        # matches the repository for older branch formats.
1402
1406
        # If looking for the code that cross-checks repository parents against
1403
 
        # the Graph.iter_lefthand_ancestry output, that is now a repository
 
1407
        # the iter_reverse_revision_history output, that is now a repository
1404
1408
        # specific check.
1405
1409
        return result
1406
1410
 
1407
 
    def _get_checkout_format(self, lightweight=False):
 
1411
    def _get_checkout_format(self):
1408
1412
        """Return the most suitable metadir for a checkout of this branch.
1409
1413
        Weaves are used if this branch's repository uses weaves.
1410
1414
        """
1445
1449
        :param to_location: The url to produce the checkout at
1446
1450
        :param revision_id: The revision to check out
1447
1451
        :param lightweight: If True, produce a lightweight checkout, otherwise,
1448
 
            produce a bound branch (heavyweight checkout)
 
1452
        produce a bound branch (heavyweight checkout)
1449
1453
        :param accelerator_tree: A tree which can be used for retrieving file
1450
1454
            contents more quickly than the revision tree, i.e. a workingtree.
1451
1455
            The revision tree will be used for cases where accelerator_tree's
1456
1460
        """
1457
1461
        t = transport.get_transport(to_location)
1458
1462
        t.ensure_base()
1459
 
        format = self._get_checkout_format(lightweight=lightweight)
1460
 
        try:
 
1463
        if lightweight:
 
1464
            format = self._get_checkout_format()
1461
1465
            checkout = format.initialize_on_transport(t)
1462
 
        except errors.AlreadyControlDirError:
1463
 
            # It's fine if the control directory already exists,
1464
 
            # as long as there is no existing branch and working tree.
1465
 
            checkout = controldir.ControlDir.open_from_transport(t)
1466
 
            try:
1467
 
                checkout.open_branch()
1468
 
            except errors.NotBranchError:
1469
 
                pass
1470
 
            else:
1471
 
                raise errors.AlreadyControlDirError(t.base)
1472
 
            if checkout.control_transport.base == self.bzrdir.control_transport.base:
1473
 
                # When checking out to the same control directory,
1474
 
                # always create a lightweight checkout
1475
 
                lightweight = True
1476
 
 
1477
 
        if lightweight:
1478
 
            from_branch = checkout.set_branch_reference(target_branch=self)
 
1466
            from_branch = BranchReferenceFormat().initialize(checkout, 
 
1467
                target_branch=self)
1479
1468
        else:
1480
 
            policy = checkout.determine_repository_policy()
1481
 
            repo = policy.acquire_repository()[0]
1482
 
            checkout_branch = checkout.create_branch()
 
1469
            format = self._get_checkout_format()
 
1470
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
 
1471
                to_location, force_new_tree=False, format=format)
 
1472
            checkout = checkout_branch.bzrdir
1483
1473
            checkout_branch.bind(self)
1484
1474
            # pull up to the specified revision_id to set the initial
1485
1475
            # branch tip correctly, and seed it with history.
1486
1476
            checkout_branch.pull(self, stop_revision=revision_id)
1487
 
            from_branch = None
 
1477
            from_branch=None
1488
1478
        tree = checkout.create_workingtree(revision_id,
1489
1479
                                           from_branch=from_branch,
1490
1480
                                           accelerator_tree=accelerator_tree,
1511
1501
 
1512
1502
    def reference_parent(self, file_id, path, possible_transports=None):
1513
1503
        """Return the parent branch for a tree-reference file_id
1514
 
 
1515
1504
        :param file_id: The file_id of the tree reference
1516
1505
        :param path: The path of the file_id in the tree
1517
1506
        :return: A branch associated with the file_id
1570
1559
        else:
1571
1560
            raise AssertionError("invalid heads: %r" % (heads,))
1572
1561
 
1573
 
    def heads_to_fetch(self):
1574
 
        """Return the heads that must and that should be fetched to copy this
1575
 
        branch into another repo.
1576
 
 
1577
 
        :returns: a 2-tuple of (must_fetch, if_present_fetch).  must_fetch is a
1578
 
            set of heads that must be fetched.  if_present_fetch is a set of
1579
 
            heads that must be fetched if present, but no error is necessary if
1580
 
            they are not present.
1581
 
        """
1582
 
        # For bzr native formats must_fetch is just the tip, and
1583
 
        # if_present_fetch are the tags.
1584
 
        must_fetch = set([self.last_revision()])
1585
 
        if_present_fetch = set()
1586
 
        if self.get_config_stack().get('branch.fetch_tags'):
1587
 
            try:
1588
 
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
1589
 
            except errors.TagsNotSupported:
1590
 
                pass
1591
 
        must_fetch.discard(_mod_revision.NULL_REVISION)
1592
 
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
1593
 
        return must_fetch, if_present_fetch
1594
 
 
1595
 
 
1596
 
class BranchFormat(controldir.ControlComponentFormat):
 
1562
 
 
1563
class BranchFormat(object):
1597
1564
    """An encapsulation of the initialization and open routines for a format.
1598
1565
 
1599
1566
    Formats provide three things:
1600
1567
     * An initialization routine,
1601
 
     * a format description
 
1568
     * a format string,
1602
1569
     * an open routine.
1603
1570
 
1604
1571
    Formats are placed in an dict by their format string for reference
1611
1578
    object will be created every time regardless.
1612
1579
    """
1613
1580
 
 
1581
    _default_format = None
 
1582
    """The default format used for new branches."""
 
1583
 
 
1584
    _formats = {}
 
1585
    """The known formats."""
 
1586
 
 
1587
    _extra_formats = []
 
1588
    """Extra formats that can not be part of a metadir."""
 
1589
 
 
1590
    can_set_append_revisions_only = True
 
1591
 
1614
1592
    def __eq__(self, other):
1615
1593
        return self.__class__ is other.__class__
1616
1594
 
1618
1596
        return not (self == other)
1619
1597
 
1620
1598
    @classmethod
1621
 
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1599
    def find_format(klass, a_bzrdir, name=None):
 
1600
        """Return the format for the branch object in a_bzrdir."""
 
1601
        try:
 
1602
            transport = a_bzrdir.get_branch_transport(None, name=name)
 
1603
            format_string = transport.get_bytes("format")
 
1604
            format = klass._formats[format_string]
 
1605
            if isinstance(format, MetaDirBranchFormatFactory):
 
1606
                return format()
 
1607
            return format
 
1608
        except errors.NoSuchFile:
 
1609
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
 
1610
        except KeyError:
 
1611
            raise errors.UnknownFormatError(format=format_string, kind='branch')
 
1612
 
 
1613
    @classmethod
1622
1614
    def get_default_format(klass):
1623
1615
        """Return the current default format."""
1624
 
        return format_registry.get_default()
 
1616
        return klass._default_format
1625
1617
 
1626
1618
    @classmethod
1627
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1628
1619
    def get_formats(klass):
1629
1620
        """Get all the known formats.
1630
1621
 
1631
1622
        Warning: This triggers a load of all lazy registered formats: do not
1632
1623
        use except when that is desireed.
1633
1624
        """
1634
 
        return format_registry._get_all()
 
1625
        result = []
 
1626
        for fmt in klass._formats.values():
 
1627
            if isinstance(fmt, MetaDirBranchFormatFactory):
 
1628
                fmt = fmt()
 
1629
            result.append(fmt)
 
1630
        return result + klass._extra_formats
1635
1631
 
1636
 
    def get_reference(self, controldir, name=None):
1637
 
        """Get the target reference of the branch in controldir.
 
1632
    def get_reference(self, a_bzrdir, name=None):
 
1633
        """Get the target reference of the branch in a_bzrdir.
1638
1634
 
1639
1635
        format probing must have been completed before calling
1640
1636
        this method - it is assumed that the format of the branch
1641
 
        in controldir is correct.
 
1637
        in a_bzrdir is correct.
1642
1638
 
1643
 
        :param controldir: The controldir to get the branch data from.
 
1639
        :param a_bzrdir: The bzrdir to get the branch data from.
1644
1640
        :param name: Name of the colocated branch to fetch
1645
1641
        :return: None if the branch is not a reference branch.
1646
1642
        """
1647
1643
        return None
1648
1644
 
1649
1645
    @classmethod
1650
 
    def set_reference(self, controldir, name, to_branch):
1651
 
        """Set the target reference of the branch in controldir.
 
1646
    def set_reference(self, a_bzrdir, name, to_branch):
 
1647
        """Set the target reference of the branch in a_bzrdir.
1652
1648
 
1653
1649
        format probing must have been completed before calling
1654
1650
        this method - it is assumed that the format of the branch
1655
 
        in controldir is correct.
 
1651
        in a_bzrdir is correct.
1656
1652
 
1657
 
        :param controldir: The controldir to set the branch reference for.
 
1653
        :param a_bzrdir: The bzrdir to set the branch reference for.
1658
1654
        :param name: Name of colocated branch to set, None for default
1659
1655
        :param to_branch: branch that the checkout is to reference
1660
1656
        """
1661
1657
        raise NotImplementedError(self.set_reference)
1662
1658
 
 
1659
    def get_format_string(self):
 
1660
        """Return the ASCII format string that identifies this format."""
 
1661
        raise NotImplementedError(self.get_format_string)
 
1662
 
1663
1663
    def get_format_description(self):
1664
1664
        """Return the short format description for this format."""
1665
1665
        raise NotImplementedError(self.get_format_description)
1666
1666
 
1667
 
    def _run_post_branch_init_hooks(self, controldir, name, branch):
 
1667
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
1668
1668
        hooks = Branch.hooks['post_branch_init']
1669
1669
        if not hooks:
1670
1670
            return
1671
 
        params = BranchInitHookParams(self, controldir, name, branch)
 
1671
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
1672
1672
        for hook in hooks:
1673
1673
            hook(params)
1674
1674
 
1675
 
    def initialize(self, controldir, name=None, repository=None,
1676
 
                   append_revisions_only=None):
1677
 
        """Create a branch of this format in controldir.
1678
 
 
 
1675
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
1676
                           repository=None):
 
1677
        """Initialize a branch in a bzrdir, with specified files
 
1678
 
 
1679
        :param a_bzrdir: The bzrdir to initialize the branch in
 
1680
        :param utf8_files: The files to create as a list of
 
1681
            (filename, content) tuples
 
1682
        :param name: Name of colocated branch to create, if any
 
1683
        :return: a branch in this format
 
1684
        """
 
1685
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
1686
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
 
1687
        control_files = lockable_files.LockableFiles(branch_transport,
 
1688
            'lock', lockdir.LockDir)
 
1689
        control_files.create_lock()
 
1690
        control_files.lock_write()
 
1691
        try:
 
1692
            utf8_files += [('format', self.get_format_string())]
 
1693
            for (filename, content) in utf8_files:
 
1694
                branch_transport.put_bytes(
 
1695
                    filename, content,
 
1696
                    mode=a_bzrdir._get_file_mode())
 
1697
        finally:
 
1698
            control_files.unlock()
 
1699
        branch = self.open(a_bzrdir, name, _found=True,
 
1700
                found_repository=repository)
 
1701
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
1702
        return branch
 
1703
 
 
1704
    def initialize(self, a_bzrdir, name=None, repository=None):
 
1705
        """Create a branch of this format in a_bzrdir.
 
1706
        
1679
1707
        :param name: Name of the colocated branch to create.
1680
1708
        """
1681
1709
        raise NotImplementedError(self.initialize)
1701
1729
        Note that it is normal for branch to be a RemoteBranch when using tags
1702
1730
        on a RemoteBranch.
1703
1731
        """
1704
 
        return _mod_tag.DisabledTags(branch)
 
1732
        return DisabledTags(branch)
1705
1733
 
1706
1734
    def network_name(self):
1707
1735
        """A simple byte string uniquely identifying this format for RPC calls.
1713
1741
        """
1714
1742
        raise NotImplementedError(self.network_name)
1715
1743
 
1716
 
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
1717
 
            found_repository=None, possible_transports=None):
1718
 
        """Return the branch object for controldir.
 
1744
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
 
1745
            found_repository=None):
 
1746
        """Return the branch object for a_bzrdir
1719
1747
 
1720
 
        :param controldir: A ControlDir that contains a branch.
 
1748
        :param a_bzrdir: A BzrDir that contains a branch.
1721
1749
        :param name: Name of colocated branch to open
1722
1750
        :param _found: a private parameter, do not use it. It is used to
1723
1751
            indicate if format probing has already be done.
1727
1755
        raise NotImplementedError(self.open)
1728
1756
 
1729
1757
    @classmethod
1730
 
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1758
    def register_extra_format(klass, format):
 
1759
        """Register a branch format that can not be part of a metadir.
 
1760
 
 
1761
        This is mainly useful to allow custom branch formats, such as
 
1762
        older Bazaar formats and foreign formats, to be tested
 
1763
        """
 
1764
        klass._extra_formats.append(format)
 
1765
        network_format_registry.register(
 
1766
            format.network_name(), format.__class__)
 
1767
 
 
1768
    @classmethod
1731
1769
    def register_format(klass, format):
1732
1770
        """Register a metadir format.
1733
 
 
 
1771
        
1734
1772
        See MetaDirBranchFormatFactory for the ability to register a format
1735
1773
        without loading the code the format needs until it is actually used.
1736
1774
        """
1737
 
        format_registry.register(format)
 
1775
        klass._formats[format.get_format_string()] = format
 
1776
        # Metadir formats have a network name of their format string, and get
 
1777
        # registered as factories.
 
1778
        if isinstance(format, MetaDirBranchFormatFactory):
 
1779
            network_format_registry.register(format.get_format_string(), format)
 
1780
        else:
 
1781
            network_format_registry.register(format.get_format_string(),
 
1782
                format.__class__)
1738
1783
 
1739
1784
    @classmethod
1740
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1741
1785
    def set_default_format(klass, format):
1742
 
        format_registry.set_default(format)
 
1786
        klass._default_format = format
1743
1787
 
1744
1788
    def supports_set_append_revisions_only(self):
1745
1789
        """True if this format supports set_append_revisions_only."""
1749
1793
        """True if this format records a stacked-on branch."""
1750
1794
        return False
1751
1795
 
1752
 
    def supports_leaving_lock(self):
1753
 
        """True if this format supports leaving locks in place."""
1754
 
        return False # by default
1755
 
 
1756
1796
    @classmethod
1757
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1758
1797
    def unregister_format(klass, format):
1759
 
        format_registry.remove(format)
 
1798
        del klass._formats[format.get_format_string()]
 
1799
 
 
1800
    @classmethod
 
1801
    def unregister_extra_format(klass, format):
 
1802
        klass._extra_formats.remove(format)
1760
1803
 
1761
1804
    def __str__(self):
1762
1805
        return self.get_format_description().rstrip()
1765
1808
        """True if this format supports tags stored in the branch"""
1766
1809
        return False  # by default
1767
1810
 
1768
 
    def tags_are_versioned(self):
1769
 
        """Whether the tag container for this branch versions tags."""
1770
 
        return False
1771
 
 
1772
 
    def supports_tags_referencing_ghosts(self):
1773
 
        """True if tags can reference ghost revisions."""
1774
 
        return True
1775
 
 
1776
1811
 
1777
1812
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1778
1813
    """A factory for a BranchFormat object, permitting simple lazy registration.
1792
1827
        """
1793
1828
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
1794
1829
        self._format_string = format_string
1795
 
 
 
1830
        
1796
1831
    def get_format_string(self):
1797
1832
        """See BranchFormat.get_format_string."""
1798
1833
        return self._format_string
1815
1850
        These are all empty initially, because by default nothing should get
1816
1851
        notified.
1817
1852
        """
1818
 
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1819
 
        self.add_hook('set_rh',
 
1853
        Hooks.__init__(self)
 
1854
        self.create_hook(HookPoint('set_rh',
1820
1855
            "Invoked whenever the revision history has been set via "
1821
1856
            "set_revision_history. The api signature is (branch, "
1822
1857
            "revision_history), and the branch will be write-locked. "
1823
1858
            "The set_rh hook can be expensive for bzr to trigger, a better "
1824
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
1825
 
        self.add_hook('open',
 
1859
            "hook to use is Branch.post_change_branch_tip.", (0, 15), None))
 
1860
        self.create_hook(HookPoint('open',
1826
1861
            "Called with the Branch object that has been opened after a "
1827
 
            "branch is opened.", (1, 8))
1828
 
        self.add_hook('post_push',
 
1862
            "branch is opened.", (1, 8), None))
 
1863
        self.create_hook(HookPoint('post_push',
1829
1864
            "Called after a push operation completes. post_push is called "
1830
1865
            "with a bzrlib.branch.BranchPushResult object and only runs in the "
1831
 
            "bzr client.", (0, 15))
1832
 
        self.add_hook('post_pull',
 
1866
            "bzr client.", (0, 15), None))
 
1867
        self.create_hook(HookPoint('post_pull',
1833
1868
            "Called after a pull operation completes. post_pull is called "
1834
1869
            "with a bzrlib.branch.PullResult object and only runs in the "
1835
 
            "bzr client.", (0, 15))
1836
 
        self.add_hook('pre_commit',
 
1870
            "bzr client.", (0, 15), None))
 
1871
        self.create_hook(HookPoint('pre_commit',
1837
1872
            "Called after a commit is calculated but before it is "
1838
1873
            "completed. pre_commit is called with (local, master, old_revno, "
1839
1874
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1842
1877
            "basis revision. hooks MUST NOT modify this delta. "
1843
1878
            " future_tree is an in-memory tree obtained from "
1844
1879
            "CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1845
 
            "tree.", (0,91))
1846
 
        self.add_hook('post_commit',
 
1880
            "tree.", (0,91), None))
 
1881
        self.create_hook(HookPoint('post_commit',
1847
1882
            "Called in the bzr client after a commit has completed. "
1848
1883
            "post_commit is called with (local, master, old_revno, old_revid, "
1849
1884
            "new_revno, new_revid). old_revid is NULL_REVISION for the first "
1850
 
            "commit to a branch.", (0, 15))
1851
 
        self.add_hook('post_uncommit',
 
1885
            "commit to a branch.", (0, 15), None))
 
1886
        self.create_hook(HookPoint('post_uncommit',
1852
1887
            "Called in the bzr client after an uncommit completes. "
1853
1888
            "post_uncommit is called with (local, master, old_revno, "
1854
1889
            "old_revid, new_revno, new_revid) where local is the local branch "
1855
1890
            "or None, master is the target branch, and an empty branch "
1856
 
            "receives new_revno of 0, new_revid of None.", (0, 15))
1857
 
        self.add_hook('pre_change_branch_tip',
 
1891
            "receives new_revno of 0, new_revid of None.", (0, 15), None))
 
1892
        self.create_hook(HookPoint('pre_change_branch_tip',
1858
1893
            "Called in bzr client and server before a change to the tip of a "
1859
1894
            "branch is made. pre_change_branch_tip is called with a "
1860
1895
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1861
 
            "commit, uncommit will all trigger this hook.", (1, 6))
1862
 
        self.add_hook('post_change_branch_tip',
 
1896
            "commit, uncommit will all trigger this hook.", (1, 6), None))
 
1897
        self.create_hook(HookPoint('post_change_branch_tip',
1863
1898
            "Called in bzr client and server after a change to the tip of a "
1864
1899
            "branch is made. post_change_branch_tip is called with a "
1865
1900
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1866
 
            "commit, uncommit will all trigger this hook.", (1, 4))
1867
 
        self.add_hook('transform_fallback_location',
 
1901
            "commit, uncommit will all trigger this hook.", (1, 4), None))
 
1902
        self.create_hook(HookPoint('transform_fallback_location',
1868
1903
            "Called when a stacked branch is activating its fallback "
1869
1904
            "locations. transform_fallback_location is called with (branch, "
1870
1905
            "url), and should return a new url. Returning the same url "
1875
1910
            "fallback locations have not been activated. When there are "
1876
1911
            "multiple hooks installed for transform_fallback_location, "
1877
1912
            "all are called with the url returned from the previous hook."
1878
 
            "The order is however undefined.", (1, 9))
1879
 
        self.add_hook('automatic_tag_name',
 
1913
            "The order is however undefined.", (1, 9), None))
 
1914
        self.create_hook(HookPoint('automatic_tag_name',
1880
1915
            "Called to determine an automatic tag name for a revision. "
1881
1916
            "automatic_tag_name is called with (branch, revision_id) and "
1882
1917
            "should return a tag name or None if no tag name could be "
1883
1918
            "determined. The first non-None tag name returned will be used.",
1884
 
            (2, 2))
1885
 
        self.add_hook('post_branch_init',
 
1919
            (2, 2), None))
 
1920
        self.create_hook(HookPoint('post_branch_init',
1886
1921
            "Called after new branch initialization completes. "
1887
1922
            "post_branch_init is called with a "
1888
1923
            "bzrlib.branch.BranchInitHookParams. "
1889
1924
            "Note that init, branch and checkout (both heavyweight and "
1890
 
            "lightweight) will all trigger this hook.", (2, 2))
1891
 
        self.add_hook('post_switch',
 
1925
            "lightweight) will all trigger this hook.", (2, 2), None))
 
1926
        self.create_hook(HookPoint('post_switch',
1892
1927
            "Called after a checkout switches branch. "
1893
1928
            "post_switch is called with a "
1894
 
            "bzrlib.branch.SwitchHookParams.", (2, 2))
 
1929
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
1895
1930
 
1896
1931
 
1897
1932
 
1900
1935
 
1901
1936
 
1902
1937
class ChangeBranchTipParams(object):
1903
 
    """Object holding parameters passed to `*_change_branch_tip` hooks.
 
1938
    """Object holding parameters passed to *_change_branch_tip hooks.
1904
1939
 
1905
1940
    There are 5 fields that hooks may wish to access:
1906
1941
 
1938
1973
 
1939
1974
 
1940
1975
class BranchInitHookParams(object):
1941
 
    """Object holding parameters passed to `*_branch_init` hooks.
 
1976
    """Object holding parameters passed to *_branch_init hooks.
1942
1977
 
1943
1978
    There are 4 fields that hooks may wish to access:
1944
1979
 
1945
1980
    :ivar format: the branch format
1946
 
    :ivar bzrdir: the ControlDir where the branch will be/has been initialized
 
1981
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
1947
1982
    :ivar name: name of colocated branch, if any (or None)
1948
1983
    :ivar branch: the branch created
1949
1984
 
1952
1987
    branch, which refer to the original branch.
1953
1988
    """
1954
1989
 
1955
 
    def __init__(self, format, controldir, name, branch):
 
1990
    def __init__(self, format, a_bzrdir, name, branch):
1956
1991
        """Create a group of BranchInitHook parameters.
1957
1992
 
1958
1993
        :param format: the branch format
1959
 
        :param controldir: the ControlDir where the branch will be/has been
 
1994
        :param a_bzrdir: the BzrDir where the branch will be/has been
1960
1995
            initialized
1961
1996
        :param name: name of colocated branch, if any (or None)
1962
1997
        :param branch: the branch created
1966
2001
        in branch, which refer to the original branch.
1967
2002
        """
1968
2003
        self.format = format
1969
 
        self.bzrdir = controldir
 
2004
        self.bzrdir = a_bzrdir
1970
2005
        self.name = name
1971
2006
        self.branch = branch
1972
2007
 
1978
2013
 
1979
2014
 
1980
2015
class SwitchHookParams(object):
1981
 
    """Object holding parameters passed to `*_switch` hooks.
 
2016
    """Object holding parameters passed to *_switch hooks.
1982
2017
 
1983
2018
    There are 4 fields that hooks may wish to access:
1984
2019
 
1985
 
    :ivar control_dir: ControlDir of the checkout to change
 
2020
    :ivar control_dir: BzrDir of the checkout to change
1986
2021
    :ivar to_branch: branch that the checkout is to reference
1987
2022
    :ivar force: skip the check for local commits in a heavy checkout
1988
2023
    :ivar revision_id: revision ID to switch to (or None)
1991
2026
    def __init__(self, control_dir, to_branch, force, revision_id):
1992
2027
        """Create a group of SwitchHook parameters.
1993
2028
 
1994
 
        :param control_dir: ControlDir of the checkout to change
 
2029
        :param control_dir: BzrDir of the checkout to change
1995
2030
        :param to_branch: branch that the checkout is to reference
1996
2031
        :param force: skip the check for local commits in a heavy checkout
1997
2032
        :param revision_id: revision ID to switch to (or None)
2010
2045
            self.revision_id)
2011
2046
 
2012
2047
 
2013
 
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
2014
 
    """Base class for branch formats that live in meta directories.
2015
 
    """
2016
 
 
2017
 
    def __init__(self):
2018
 
        BranchFormat.__init__(self)
2019
 
        bzrdir.BzrFormat.__init__(self)
2020
 
 
2021
 
    @classmethod
2022
 
    def find_format(klass, controldir, name=None):
2023
 
        """Return the format for the branch object in controldir."""
2024
 
        try:
2025
 
            transport = controldir.get_branch_transport(None, name=name)
2026
 
        except errors.NoSuchFile:
2027
 
            raise errors.NotBranchError(path=name, bzrdir=controldir)
2028
 
        try:
2029
 
            format_string = transport.get_bytes("format")
2030
 
        except errors.NoSuchFile:
2031
 
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
2032
 
        return klass._find_format(format_registry, 'branch', format_string)
 
2048
class BranchFormatMetadir(BranchFormat):
 
2049
    """Common logic for meta-dir based branch formats."""
2033
2050
 
2034
2051
    def _branch_class(self):
2035
2052
        """What class to instantiate on open calls."""
2036
2053
        raise NotImplementedError(self._branch_class)
2037
2054
 
2038
 
    def _get_initial_config(self, append_revisions_only=None):
2039
 
        if append_revisions_only:
2040
 
            return "append_revisions_only = True\n"
2041
 
        else:
2042
 
            # Avoid writing anything if append_revisions_only is disabled,
2043
 
            # as that is the default.
2044
 
            return ""
2045
 
 
2046
 
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
2047
 
                           repository=None):
2048
 
        """Initialize a branch in a bzrdir, with specified files
2049
 
 
2050
 
        :param a_bzrdir: The bzrdir to initialize the branch in
2051
 
        :param utf8_files: The files to create as a list of
2052
 
            (filename, content) tuples
2053
 
        :param name: Name of colocated branch to create, if any
2054
 
        :return: a branch in this format
 
2055
    def network_name(self):
 
2056
        """A simple byte string uniquely identifying this format for RPC calls.
 
2057
 
 
2058
        Metadir branch formats use their format string.
2055
2059
        """
2056
 
        if name is None:
2057
 
            name = a_bzrdir._get_selected_branch()
2058
 
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
2059
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2060
 
        control_files = lockable_files.LockableFiles(branch_transport,
2061
 
            'lock', lockdir.LockDir)
2062
 
        control_files.create_lock()
2063
 
        control_files.lock_write()
2064
 
        try:
2065
 
            utf8_files += [('format', self.as_string())]
2066
 
            for (filename, content) in utf8_files:
2067
 
                branch_transport.put_bytes(
2068
 
                    filename, content,
2069
 
                    mode=a_bzrdir._get_file_mode())
2070
 
        finally:
2071
 
            control_files.unlock()
2072
 
        branch = self.open(a_bzrdir, name, _found=True,
2073
 
                found_repository=repository)
2074
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2075
 
        return branch
 
2060
        return self.get_format_string()
2076
2061
 
2077
2062
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2078
 
            found_repository=None, possible_transports=None):
 
2063
            found_repository=None):
2079
2064
        """See BranchFormat.open()."""
2080
 
        if name is None:
2081
 
            name = a_bzrdir._get_selected_branch()
2082
2065
        if not _found:
2083
 
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
 
2066
            format = BranchFormat.find_format(a_bzrdir, name=name)
2084
2067
            if format.__class__ != self.__class__:
2085
2068
                raise AssertionError("wrong format %r found for %r" %
2086
2069
                    (format, self))
2095
2078
                              name=name,
2096
2079
                              a_bzrdir=a_bzrdir,
2097
2080
                              _repository=found_repository,
2098
 
                              ignore_fallbacks=ignore_fallbacks,
2099
 
                              possible_transports=possible_transports)
 
2081
                              ignore_fallbacks=ignore_fallbacks)
2100
2082
        except errors.NoSuchFile:
2101
2083
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2102
2084
 
2103
 
    @property
2104
 
    def _matchingbzrdir(self):
2105
 
        ret = bzrdir.BzrDirMetaFormat1()
2106
 
        ret.set_branch_format(self)
2107
 
        return ret
 
2085
    def __init__(self):
 
2086
        super(BranchFormatMetadir, self).__init__()
 
2087
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
2088
        self._matchingbzrdir.set_branch_format(self)
2108
2089
 
2109
2090
    def supports_tags(self):
2110
2091
        return True
2111
2092
 
2112
 
    def supports_leaving_lock(self):
2113
 
        return True
2114
 
 
2115
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
2116
 
            basedir=None):
2117
 
        BranchFormat.check_support_status(self,
2118
 
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
2119
 
            basedir=basedir)
2120
 
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
2121
 
            recommend_upgrade=recommend_upgrade, basedir=basedir)
2122
 
 
2123
2093
 
2124
2094
class BzrBranchFormat5(BranchFormatMetadir):
2125
2095
    """Bzr branch format 5.
2137
2107
    def _branch_class(self):
2138
2108
        return BzrBranch5
2139
2109
 
2140
 
    @classmethod
2141
 
    def get_format_string(cls):
 
2110
    def get_format_string(self):
2142
2111
        """See BranchFormat.get_format_string()."""
2143
2112
        return "Bazaar-NG branch format 5\n"
2144
2113
 
2146
2115
        """See BranchFormat.get_format_description()."""
2147
2116
        return "Branch format 5"
2148
2117
 
2149
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2150
 
                   append_revisions_only=None):
 
2118
    def initialize(self, a_bzrdir, name=None, repository=None):
2151
2119
        """Create a branch of this format in a_bzrdir."""
2152
 
        if append_revisions_only:
2153
 
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2154
2120
        utf8_files = [('revision-history', ''),
2155
2121
                      ('branch-name', ''),
2156
2122
                      ]
2174
2140
    def _branch_class(self):
2175
2141
        return BzrBranch6
2176
2142
 
2177
 
    @classmethod
2178
 
    def get_format_string(cls):
 
2143
    def get_format_string(self):
2179
2144
        """See BranchFormat.get_format_string()."""
2180
2145
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
2181
2146
 
2183
2148
        """See BranchFormat.get_format_description()."""
2184
2149
        return "Branch format 6"
2185
2150
 
2186
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2187
 
                   append_revisions_only=None):
 
2151
    def initialize(self, a_bzrdir, name=None, repository=None):
2188
2152
        """Create a branch of this format in a_bzrdir."""
2189
2153
        utf8_files = [('last-revision', '0 null:\n'),
2190
 
                      ('branch.conf',
2191
 
                          self._get_initial_config(append_revisions_only)),
 
2154
                      ('branch.conf', ''),
2192
2155
                      ('tags', ''),
2193
2156
                      ]
2194
2157
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2195
2158
 
2196
2159
    def make_tags(self, branch):
2197
2160
        """See bzrlib.branch.BranchFormat.make_tags()."""
2198
 
        return _mod_tag.BasicTags(branch)
 
2161
        return BasicTags(branch)
2199
2162
 
2200
2163
    def supports_set_append_revisions_only(self):
2201
2164
        return True
2207
2170
    def _branch_class(self):
2208
2171
        return BzrBranch8
2209
2172
 
2210
 
    @classmethod
2211
 
    def get_format_string(cls):
 
2173
    def get_format_string(self):
2212
2174
        """See BranchFormat.get_format_string()."""
2213
2175
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2214
2176
 
2216
2178
        """See BranchFormat.get_format_description()."""
2217
2179
        return "Branch format 8"
2218
2180
 
2219
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2220
 
                   append_revisions_only=None):
 
2181
    def initialize(self, a_bzrdir, name=None, repository=None):
2221
2182
        """Create a branch of this format in a_bzrdir."""
2222
2183
        utf8_files = [('last-revision', '0 null:\n'),
2223
 
                      ('branch.conf',
2224
 
                          self._get_initial_config(append_revisions_only)),
 
2184
                      ('branch.conf', ''),
2225
2185
                      ('tags', ''),
2226
2186
                      ('references', '')
2227
2187
                      ]
2228
2188
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2229
2189
 
 
2190
    def __init__(self):
 
2191
        super(BzrBranchFormat8, self).__init__()
 
2192
        self._matchingbzrdir.repository_format = \
 
2193
            RepositoryFormatKnitPack5RichRoot()
 
2194
 
2230
2195
    def make_tags(self, branch):
2231
2196
        """See bzrlib.branch.BranchFormat.make_tags()."""
2232
 
        return _mod_tag.BasicTags(branch)
 
2197
        return BasicTags(branch)
2233
2198
 
2234
2199
    def supports_set_append_revisions_only(self):
2235
2200
        return True
2240
2205
    supports_reference_locations = True
2241
2206
 
2242
2207
 
2243
 
class BzrBranchFormat7(BranchFormatMetadir):
 
2208
class BzrBranchFormat7(BzrBranchFormat8):
2244
2209
    """Branch format with last-revision, tags, and a stacked location pointer.
2245
2210
 
2246
2211
    The stacked location pointer is passed down to the repository and requires
2249
2214
    This format was introduced in bzr 1.6.
2250
2215
    """
2251
2216
 
2252
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2253
 
                   append_revisions_only=None):
 
2217
    def initialize(self, a_bzrdir, name=None, repository=None):
2254
2218
        """Create a branch of this format in a_bzrdir."""
2255
2219
        utf8_files = [('last-revision', '0 null:\n'),
2256
 
                      ('branch.conf',
2257
 
                          self._get_initial_config(append_revisions_only)),
 
2220
                      ('branch.conf', ''),
2258
2221
                      ('tags', ''),
2259
2222
                      ]
2260
2223
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2262
2225
    def _branch_class(self):
2263
2226
        return BzrBranch7
2264
2227
 
2265
 
    @classmethod
2266
 
    def get_format_string(cls):
 
2228
    def get_format_string(self):
2267
2229
        """See BranchFormat.get_format_string()."""
2268
2230
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2269
2231
 
2274
2236
    def supports_set_append_revisions_only(self):
2275
2237
        return True
2276
2238
 
2277
 
    def supports_stacking(self):
2278
 
        return True
2279
 
 
2280
 
    def make_tags(self, branch):
2281
 
        """See bzrlib.branch.BranchFormat.make_tags()."""
2282
 
        return _mod_tag.BasicTags(branch)
2283
 
 
2284
2239
    supports_reference_locations = False
2285
2240
 
2286
2241
 
2287
 
class BranchReferenceFormat(BranchFormatMetadir):
 
2242
class BranchReferenceFormat(BranchFormat):
2288
2243
    """Bzr branch reference format.
2289
2244
 
2290
2245
    Branch references are used in implementing checkouts, they
2295
2250
     - a format string
2296
2251
    """
2297
2252
 
2298
 
    @classmethod
2299
 
    def get_format_string(cls):
 
2253
    def get_format_string(self):
2300
2254
        """See BranchFormat.get_format_string()."""
2301
2255
        return "Bazaar-NG Branch Reference Format 1\n"
2302
2256
 
2315
2269
        location = transport.put_bytes('location', to_branch.base)
2316
2270
 
2317
2271
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2318
 
            repository=None, append_revisions_only=None):
 
2272
            repository=None):
2319
2273
        """Create a branch of this format in a_bzrdir."""
2320
2274
        if target_branch is None:
2321
2275
            # this format does not implement branch itself, thus the implicit
2322
2276
            # creation contract must see it as uninitializable
2323
2277
            raise errors.UninitializableFormat(self)
2324
2278
        mutter('creating branch reference in %s', a_bzrdir.user_url)
2325
 
        if a_bzrdir._format.fixed_components:
2326
 
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
2327
 
        if name is None:
2328
 
            name = a_bzrdir._get_selected_branch()
2329
2279
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2330
2280
        branch_transport.put_bytes('location',
2331
 
            target_branch.user_url)
2332
 
        branch_transport.put_bytes('format', self.as_string())
2333
 
        branch = self.open(a_bzrdir, name, _found=True,
 
2281
            target_branch.bzrdir.user_url)
 
2282
        branch_transport.put_bytes('format', self.get_format_string())
 
2283
        branch = self.open(
 
2284
            a_bzrdir, name, _found=True,
2334
2285
            possible_transports=[target_branch.bzrdir.root_transport])
2335
2286
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2336
2287
        return branch
2337
2288
 
 
2289
    def __init__(self):
 
2290
        super(BranchReferenceFormat, self).__init__()
 
2291
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
2292
        self._matchingbzrdir.set_branch_format(self)
 
2293
 
2338
2294
    def _make_reference_clone_function(format, a_branch):
2339
2295
        """Create a clone() routine for a branch dynamically."""
2340
2296
        def clone(to_bzrdir, revision_id=None,
2362
2318
            a_bzrdir.
2363
2319
        :param possible_transports: An optional reusable transports list.
2364
2320
        """
2365
 
        if name is None:
2366
 
            name = a_bzrdir._get_selected_branch()
2367
2321
        if not _found:
2368
 
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
 
2322
            format = BranchFormat.find_format(a_bzrdir, name=name)
2369
2323
            if format.__class__ != self.__class__:
2370
2324
                raise AssertionError("wrong format %r found for %r" %
2371
2325
                    (format, self))
2372
2326
        if location is None:
2373
2327
            location = self.get_reference(a_bzrdir, name)
2374
 
        real_bzrdir = controldir.ControlDir.open(
 
2328
        real_bzrdir = bzrdir.BzrDir.open(
2375
2329
            location, possible_transports=possible_transports)
2376
 
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
2377
 
            possible_transports=possible_transports)
 
2330
        result = real_bzrdir.open_branch(name=name, 
 
2331
            ignore_fallbacks=ignore_fallbacks)
2378
2332
        # this changes the behaviour of result.clone to create a new reference
2379
2333
        # rather than a copy of the content of the branch.
2380
2334
        # I did not use a proxy object because that needs much more extensive
2387
2341
        return result
2388
2342
 
2389
2343
 
2390
 
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
2391
 
    """Branch format registry."""
2392
 
 
2393
 
    def __init__(self, other_registry=None):
2394
 
        super(BranchFormatRegistry, self).__init__(other_registry)
2395
 
        self._default_format = None
2396
 
 
2397
 
    def set_default(self, format):
2398
 
        self._default_format = format
2399
 
 
2400
 
    def get_default(self):
2401
 
        return self._default_format
2402
 
 
2403
 
 
2404
2344
network_format_registry = registry.FormatRegistry()
2405
2345
"""Registry of formats indexed by their network name.
2406
2346
 
2409
2349
BranchFormat.network_name() for more detail.
2410
2350
"""
2411
2351
 
2412
 
format_registry = BranchFormatRegistry(network_format_registry)
2413
 
 
2414
2352
 
2415
2353
# formats which have no format string are not discoverable
2416
2354
# and not independently creatable, so are not registered.
2418
2356
__format6 = BzrBranchFormat6()
2419
2357
__format7 = BzrBranchFormat7()
2420
2358
__format8 = BzrBranchFormat8()
2421
 
format_registry.register(__format5)
2422
 
format_registry.register(BranchReferenceFormat())
2423
 
format_registry.register(__format6)
2424
 
format_registry.register(__format7)
2425
 
format_registry.register(__format8)
2426
 
format_registry.set_default(__format7)
 
2359
BranchFormat.register_format(__format5)
 
2360
BranchFormat.register_format(BranchReferenceFormat())
 
2361
BranchFormat.register_format(__format6)
 
2362
BranchFormat.register_format(__format7)
 
2363
BranchFormat.register_format(__format8)
 
2364
BranchFormat.set_default_format(__format7)
2427
2365
 
2428
2366
 
2429
2367
class BranchWriteLockResult(LogicalLockResult):
2461
2399
 
2462
2400
    def __init__(self, _format=None,
2463
2401
                 _control_files=None, a_bzrdir=None, name=None,
2464
 
                 _repository=None, ignore_fallbacks=False,
2465
 
                 possible_transports=None):
 
2402
                 _repository=None, ignore_fallbacks=False):
2466
2403
        """Create new branch object at a particular location."""
2467
2404
        if a_bzrdir is None:
2468
2405
            raise ValueError('a_bzrdir must be supplied')
2469
 
        if name is None:
2470
 
            raise ValueError('name must be supplied')
2471
 
        self.bzrdir = a_bzrdir
2472
 
        self._user_transport = self.bzrdir.transport.clone('..')
2473
 
        if name != "":
2474
 
            self._user_transport.set_segment_parameter(
2475
 
                "branch", urlutils.escape(name))
2476
 
        self._base = self._user_transport.base
 
2406
        else:
 
2407
            self.bzrdir = a_bzrdir
 
2408
        self._base = self.bzrdir.transport.clone('..').base
2477
2409
        self.name = name
 
2410
        # XXX: We should be able to just do
 
2411
        #   self.base = self.bzrdir.root_transport.base
 
2412
        # but this does not quite work yet -- mbp 20080522
2478
2413
        self._format = _format
2479
2414
        if _control_files is None:
2480
2415
            raise ValueError('BzrBranch _control_files is None')
2481
2416
        self.control_files = _control_files
2482
2417
        self._transport = _control_files._transport
2483
2418
        self.repository = _repository
2484
 
        self.conf_store = None
2485
 
        Branch.__init__(self, possible_transports)
 
2419
        Branch.__init__(self)
2486
2420
 
2487
2421
    def __str__(self):
2488
 
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
 
2422
        if self.name is None:
 
2423
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
 
2424
        else:
 
2425
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
 
2426
                self.name)
2489
2427
 
2490
2428
    __repr__ = __str__
2491
2429
 
2495
2433
 
2496
2434
    base = property(_get_base, doc="The URL for the root of this branch.")
2497
2435
 
2498
 
    @property
2499
 
    def user_transport(self):
2500
 
        return self._user_transport
2501
 
 
2502
2436
    def _get_config(self):
2503
 
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
2504
 
 
2505
 
    def _get_config_store(self):
2506
 
        if self.conf_store is None:
2507
 
            self.conf_store =  _mod_config.BranchStore(self)
2508
 
        return self.conf_store
 
2437
        return TransportConfig(self._transport, 'branch.conf')
2509
2438
 
2510
2439
    def is_locked(self):
2511
2440
        return self.control_files.is_locked()
2560
2489
 
2561
2490
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2562
2491
    def unlock(self):
2563
 
        if self.conf_store is not None:
2564
 
            self.conf_store.save_changes()
2565
2492
        try:
2566
2493
            self.control_files.unlock()
2567
2494
        finally:
2588
2515
        """See Branch.print_file."""
2589
2516
        return self.repository.print_file(file, revision_id)
2590
2517
 
 
2518
    def _write_revision_history(self, history):
 
2519
        """Factored out of set_revision_history.
 
2520
 
 
2521
        This performs the actual writing to disk.
 
2522
        It is intended to be called by BzrBranch5.set_revision_history."""
 
2523
        self._transport.put_bytes(
 
2524
            'revision-history', '\n'.join(history),
 
2525
            mode=self.bzrdir._get_file_mode())
 
2526
 
 
2527
    @needs_write_lock
 
2528
    def set_revision_history(self, rev_history):
 
2529
        """See Branch.set_revision_history."""
 
2530
        if 'evil' in debug.debug_flags:
 
2531
            mutter_callsite(3, "set_revision_history scales with history.")
 
2532
        check_not_reserved_id = _mod_revision.check_not_reserved_id
 
2533
        for rev_id in rev_history:
 
2534
            check_not_reserved_id(rev_id)
 
2535
        if Branch.hooks['post_change_branch_tip']:
 
2536
            # Don't calculate the last_revision_info() if there are no hooks
 
2537
            # that will use it.
 
2538
            old_revno, old_revid = self.last_revision_info()
 
2539
        if len(rev_history) == 0:
 
2540
            revid = _mod_revision.NULL_REVISION
 
2541
        else:
 
2542
            revid = rev_history[-1]
 
2543
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
 
2544
        self._write_revision_history(rev_history)
 
2545
        self._clear_cached_state()
 
2546
        self._cache_revision_history(rev_history)
 
2547
        for hook in Branch.hooks['set_rh']:
 
2548
            hook(self, rev_history)
 
2549
        if Branch.hooks['post_change_branch_tip']:
 
2550
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2551
 
 
2552
    def _synchronize_history(self, destination, revision_id):
 
2553
        """Synchronize last revision and revision history between branches.
 
2554
 
 
2555
        This version is most efficient when the destination is also a
 
2556
        BzrBranch5, but works for BzrBranch6 as long as the revision
 
2557
        history is the true lefthand parent history, and all of the revisions
 
2558
        are in the destination's repository.  If not, set_revision_history
 
2559
        will fail.
 
2560
 
 
2561
        :param destination: The branch to copy the history into
 
2562
        :param revision_id: The revision-id to truncate history at.  May
 
2563
          be None to copy complete history.
 
2564
        """
 
2565
        if not isinstance(destination._format, BzrBranchFormat5):
 
2566
            super(BzrBranch, self)._synchronize_history(
 
2567
                destination, revision_id)
 
2568
            return
 
2569
        if revision_id == _mod_revision.NULL_REVISION:
 
2570
            new_history = []
 
2571
        else:
 
2572
            new_history = self.revision_history()
 
2573
        if revision_id is not None and new_history != []:
 
2574
            try:
 
2575
                new_history = new_history[:new_history.index(revision_id) + 1]
 
2576
            except ValueError:
 
2577
                rev = self.repository.get_revision(revision_id)
 
2578
                new_history = rev.get_history(self.repository)[1:]
 
2579
        destination.set_revision_history(new_history)
 
2580
 
2591
2581
    @needs_write_lock
2592
2582
    def set_last_revision_info(self, revno, revision_id):
2593
 
        if not revision_id or not isinstance(revision_id, basestring):
2594
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2583
        """Set the last revision of this branch.
 
2584
 
 
2585
        The caller is responsible for checking that the revno is correct
 
2586
        for this revision id.
 
2587
 
 
2588
        It may be possible to set the branch last revision to an id not
 
2589
        present in the repository.  However, branches can also be
 
2590
        configured to check constraints on history, in which case this may not
 
2591
        be permitted.
 
2592
        """
2595
2593
        revision_id = _mod_revision.ensure_null(revision_id)
2596
 
        old_revno, old_revid = self.last_revision_info()
2597
 
        if self.get_append_revisions_only():
2598
 
            self._check_history_violation(revision_id)
2599
 
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2600
 
        self._write_last_revision_info(revno, revision_id)
2601
 
        self._clear_cached_state()
2602
 
        self._last_revision_info_cache = revno, revision_id
2603
 
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2594
        # this old format stores the full history, but this api doesn't
 
2595
        # provide it, so we must generate, and might as well check it's
 
2596
        # correct
 
2597
        history = self._lefthand_history(revision_id)
 
2598
        if len(history) != revno:
 
2599
            raise AssertionError('%d != %d' % (len(history), revno))
 
2600
        self.set_revision_history(history)
 
2601
 
 
2602
    def _gen_revision_history(self):
 
2603
        history = self._transport.get_bytes('revision-history').split('\n')
 
2604
        if history[-1:] == ['']:
 
2605
            # There shouldn't be a trailing newline, but just in case.
 
2606
            history.pop()
 
2607
        return history
 
2608
 
 
2609
    @needs_write_lock
 
2610
    def generate_revision_history(self, revision_id, last_rev=None,
 
2611
        other_branch=None):
 
2612
        """Create a new revision history that will finish with revision_id.
 
2613
 
 
2614
        :param revision_id: the new tip to use.
 
2615
        :param last_rev: The previous last_revision. If not None, then this
 
2616
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
2617
        :param other_branch: The other branch that DivergedBranches should
 
2618
            raise with respect to.
 
2619
        """
 
2620
        self.set_revision_history(self._lefthand_history(revision_id,
 
2621
            last_rev, other_branch))
2604
2622
 
2605
2623
    def basis_tree(self):
2606
2624
        """See Branch.basis_tree."""
2615
2633
                pass
2616
2634
        return None
2617
2635
 
 
2636
    def _basic_push(self, target, overwrite, stop_revision):
 
2637
        """Basic implementation of push without bound branches or hooks.
 
2638
 
 
2639
        Must be called with source read locked and target write locked.
 
2640
        """
 
2641
        result = BranchPushResult()
 
2642
        result.source_branch = self
 
2643
        result.target_branch = target
 
2644
        result.old_revno, result.old_revid = target.last_revision_info()
 
2645
        self.update_references(target)
 
2646
        if result.old_revid != stop_revision:
 
2647
            # We assume that during 'push' this repository is closer than
 
2648
            # the target.
 
2649
            graph = self.repository.get_graph(target.repository)
 
2650
            target.update_revisions(self, stop_revision,
 
2651
                overwrite=overwrite, graph=graph)
 
2652
        if self._push_should_merge_tags():
 
2653
            result.tag_conflicts = self.tags.merge_to(target.tags,
 
2654
                overwrite)
 
2655
        result.new_revno, result.new_revid = target.last_revision_info()
 
2656
        return result
 
2657
 
2618
2658
    def get_stacked_on_url(self):
2619
2659
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2620
2660
 
2631
2671
            self._transport.put_bytes('parent', url + '\n',
2632
2672
                mode=self.bzrdir._get_file_mode())
2633
2673
 
 
2674
 
 
2675
class BzrBranchPreSplitOut(BzrBranch):
 
2676
 
 
2677
    def _get_checkout_format(self):
 
2678
        """Return the most suitable metadir for a checkout of this branch.
 
2679
        Weaves are used if this branch's repository uses weaves.
 
2680
        """
 
2681
        from bzrlib.repofmt.weaverepo import RepositoryFormat7
 
2682
        from bzrlib.bzrdir import BzrDirMetaFormat1
 
2683
        format = BzrDirMetaFormat1()
 
2684
        format.repository_format = RepositoryFormat7()
 
2685
        return format
 
2686
 
 
2687
 
 
2688
class BzrBranch5(BzrBranch):
 
2689
    """A format 5 branch. This supports new features over plain branches.
 
2690
 
 
2691
    It has support for a master_branch which is the data for bound branches.
 
2692
    """
 
2693
 
 
2694
    def get_bound_location(self):
 
2695
        try:
 
2696
            return self._transport.get_bytes('bound')[:-1]
 
2697
        except errors.NoSuchFile:
 
2698
            return None
 
2699
 
 
2700
    @needs_read_lock
 
2701
    def get_master_branch(self, possible_transports=None):
 
2702
        """Return the branch we are bound to.
 
2703
 
 
2704
        :return: Either a Branch, or None
 
2705
 
 
2706
        This could memoise the branch, but if thats done
 
2707
        it must be revalidated on each new lock.
 
2708
        So for now we just don't memoise it.
 
2709
        # RBC 20060304 review this decision.
 
2710
        """
 
2711
        bound_loc = self.get_bound_location()
 
2712
        if not bound_loc:
 
2713
            return None
 
2714
        try:
 
2715
            return Branch.open(bound_loc,
 
2716
                               possible_transports=possible_transports)
 
2717
        except (errors.NotBranchError, errors.ConnectionError), e:
 
2718
            raise errors.BoundBranchConnectionFailure(
 
2719
                    self, bound_loc, e)
 
2720
 
2634
2721
    @needs_write_lock
2635
 
    def unbind(self):
2636
 
        """If bound, unbind"""
2637
 
        return self.set_bound_location(None)
 
2722
    def set_bound_location(self, location):
 
2723
        """Set the target where this branch is bound to.
 
2724
 
 
2725
        :param location: URL to the target branch
 
2726
        """
 
2727
        if location:
 
2728
            self._transport.put_bytes('bound', location+'\n',
 
2729
                mode=self.bzrdir._get_file_mode())
 
2730
        else:
 
2731
            try:
 
2732
                self._transport.delete('bound')
 
2733
            except errors.NoSuchFile:
 
2734
                return False
 
2735
            return True
2638
2736
 
2639
2737
    @needs_write_lock
2640
2738
    def bind(self, other):
2662
2760
        # history around
2663
2761
        self.set_bound_location(other.base)
2664
2762
 
2665
 
    def get_bound_location(self):
2666
 
        try:
2667
 
            return self._transport.get_bytes('bound')[:-1]
2668
 
        except errors.NoSuchFile:
2669
 
            return None
2670
 
 
2671
 
    @needs_read_lock
2672
 
    def get_master_branch(self, possible_transports=None):
2673
 
        """Return the branch we are bound to.
2674
 
 
2675
 
        :return: Either a Branch, or None
2676
 
        """
2677
 
        if self._master_branch_cache is None:
2678
 
            self._master_branch_cache = self._get_master_branch(
2679
 
                possible_transports)
2680
 
        return self._master_branch_cache
2681
 
 
2682
 
    def _get_master_branch(self, possible_transports):
2683
 
        bound_loc = self.get_bound_location()
2684
 
        if not bound_loc:
2685
 
            return None
2686
 
        try:
2687
 
            return Branch.open(bound_loc,
2688
 
                               possible_transports=possible_transports)
2689
 
        except (errors.NotBranchError, errors.ConnectionError), e:
2690
 
            raise errors.BoundBranchConnectionFailure(
2691
 
                    self, bound_loc, e)
2692
 
 
2693
2763
    @needs_write_lock
2694
 
    def set_bound_location(self, location):
2695
 
        """Set the target where this branch is bound to.
2696
 
 
2697
 
        :param location: URL to the target branch
2698
 
        """
2699
 
        self._master_branch_cache = None
2700
 
        if location:
2701
 
            self._transport.put_bytes('bound', location+'\n',
2702
 
                mode=self.bzrdir._get_file_mode())
2703
 
        else:
2704
 
            try:
2705
 
                self._transport.delete('bound')
2706
 
            except errors.NoSuchFile:
2707
 
                return False
2708
 
            return True
 
2764
    def unbind(self):
 
2765
        """If bound, unbind"""
 
2766
        return self.set_bound_location(None)
2709
2767
 
2710
2768
    @needs_write_lock
2711
2769
    def update(self, possible_transports=None):
2724
2782
            return old_tip
2725
2783
        return None
2726
2784
 
2727
 
    def _read_last_revision_info(self):
2728
 
        revision_string = self._transport.get_bytes('last-revision')
2729
 
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2730
 
        revision_id = cache_utf8.get_cached_utf8(revision_id)
2731
 
        revno = int(revno)
2732
 
        return revno, revision_id
2733
 
 
2734
 
    def _write_last_revision_info(self, revno, revision_id):
2735
 
        """Simply write out the revision id, with no checks.
2736
 
 
2737
 
        Use set_last_revision_info to perform this safely.
2738
 
 
2739
 
        Does not update the revision_history cache.
2740
 
        """
2741
 
        revision_id = _mod_revision.ensure_null(revision_id)
2742
 
        out_string = '%d %s\n' % (revno, revision_id)
2743
 
        self._transport.put_bytes('last-revision', out_string,
2744
 
            mode=self.bzrdir._get_file_mode())
2745
 
 
2746
 
    @needs_write_lock
2747
 
    def update_feature_flags(self, updated_flags):
2748
 
        """Update the feature flags for this branch.
2749
 
 
2750
 
        :param updated_flags: Dictionary mapping feature names to necessities
2751
 
            A necessity can be None to indicate the feature should be removed
2752
 
        """
2753
 
        self._format._update_feature_flags(updated_flags)
2754
 
        self.control_transport.put_bytes('format', self._format.as_string())
2755
 
 
2756
 
 
2757
 
class FullHistoryBzrBranch(BzrBranch):
2758
 
    """Bzr branch which contains the full revision history."""
2759
 
 
2760
 
    @needs_write_lock
2761
 
    def set_last_revision_info(self, revno, revision_id):
2762
 
        if not revision_id or not isinstance(revision_id, basestring):
2763
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2764
 
        revision_id = _mod_revision.ensure_null(revision_id)
2765
 
        # this old format stores the full history, but this api doesn't
2766
 
        # provide it, so we must generate, and might as well check it's
2767
 
        # correct
2768
 
        history = self._lefthand_history(revision_id)
2769
 
        if len(history) != revno:
2770
 
            raise AssertionError('%d != %d' % (len(history), revno))
2771
 
        self._set_revision_history(history)
2772
 
 
2773
 
    def _read_last_revision_info(self):
2774
 
        rh = self._revision_history()
2775
 
        revno = len(rh)
2776
 
        if revno:
2777
 
            return (revno, rh[-1])
2778
 
        else:
2779
 
            return (0, _mod_revision.NULL_REVISION)
2780
 
 
2781
 
    @deprecated_method(deprecated_in((2, 4, 0)))
2782
 
    @needs_write_lock
2783
 
    def set_revision_history(self, rev_history):
2784
 
        """See Branch.set_revision_history."""
2785
 
        self._set_revision_history(rev_history)
2786
 
 
2787
 
    def _set_revision_history(self, rev_history):
2788
 
        if 'evil' in debug.debug_flags:
2789
 
            mutter_callsite(3, "set_revision_history scales with history.")
2790
 
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2791
 
        for rev_id in rev_history:
2792
 
            check_not_reserved_id(rev_id)
2793
 
        if Branch.hooks['post_change_branch_tip']:
2794
 
            # Don't calculate the last_revision_info() if there are no hooks
2795
 
            # that will use it.
2796
 
            old_revno, old_revid = self.last_revision_info()
2797
 
        if len(rev_history) == 0:
2798
 
            revid = _mod_revision.NULL_REVISION
2799
 
        else:
2800
 
            revid = rev_history[-1]
2801
 
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2802
 
        self._write_revision_history(rev_history)
2803
 
        self._clear_cached_state()
2804
 
        self._cache_revision_history(rev_history)
2805
 
        for hook in Branch.hooks['set_rh']:
2806
 
            hook(self, rev_history)
2807
 
        if Branch.hooks['post_change_branch_tip']:
2808
 
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2809
 
 
2810
 
    def _write_revision_history(self, history):
2811
 
        """Factored out of set_revision_history.
2812
 
 
2813
 
        This performs the actual writing to disk.
2814
 
        It is intended to be called by set_revision_history."""
2815
 
        self._transport.put_bytes(
2816
 
            'revision-history', '\n'.join(history),
2817
 
            mode=self.bzrdir._get_file_mode())
2818
 
 
2819
 
    def _gen_revision_history(self):
2820
 
        history = self._transport.get_bytes('revision-history').split('\n')
2821
 
        if history[-1:] == ['']:
2822
 
            # There shouldn't be a trailing newline, but just in case.
2823
 
            history.pop()
2824
 
        return history
2825
 
 
2826
 
    def _synchronize_history(self, destination, revision_id):
2827
 
        if not isinstance(destination, FullHistoryBzrBranch):
2828
 
            super(BzrBranch, self)._synchronize_history(
2829
 
                destination, revision_id)
2830
 
            return
2831
 
        if revision_id == _mod_revision.NULL_REVISION:
2832
 
            new_history = []
2833
 
        else:
2834
 
            new_history = self._revision_history()
2835
 
        if revision_id is not None and new_history != []:
2836
 
            try:
2837
 
                new_history = new_history[:new_history.index(revision_id) + 1]
2838
 
            except ValueError:
2839
 
                rev = self.repository.get_revision(revision_id)
2840
 
                new_history = rev.get_history(self.repository)[1:]
2841
 
        destination._set_revision_history(new_history)
2842
 
 
2843
 
    @needs_write_lock
2844
 
    def generate_revision_history(self, revision_id, last_rev=None,
2845
 
        other_branch=None):
2846
 
        """Create a new revision history that will finish with revision_id.
2847
 
 
2848
 
        :param revision_id: the new tip to use.
2849
 
        :param last_rev: The previous last_revision. If not None, then this
2850
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
2851
 
        :param other_branch: The other branch that DivergedBranches should
2852
 
            raise with respect to.
2853
 
        """
2854
 
        self._set_revision_history(self._lefthand_history(revision_id,
2855
 
            last_rev, other_branch))
2856
 
 
2857
 
 
2858
 
class BzrBranch5(FullHistoryBzrBranch):
2859
 
    """A format 5 branch. This supports new features over plain branches.
2860
 
 
2861
 
    It has support for a master_branch which is the data for bound branches.
2862
 
    """
2863
 
 
2864
 
 
2865
 
class BzrBranch8(BzrBranch):
 
2785
 
 
2786
class BzrBranch8(BzrBranch5):
2866
2787
    """A branch that stores tree-reference locations."""
2867
2788
 
2868
 
    def _open_hook(self, possible_transports=None):
 
2789
    def _open_hook(self):
2869
2790
        if self._ignore_fallbacks:
2870
2791
            return
2871
 
        if possible_transports is None:
2872
 
            possible_transports = [self.bzrdir.root_transport]
2873
2792
        try:
2874
2793
            url = self.get_stacked_on_url()
2875
2794
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2883
2802
                    raise AssertionError(
2884
2803
                        "'transform_fallback_location' hook %s returned "
2885
2804
                        "None, not a URL." % hook_name)
2886
 
            self._activate_fallback_location(url,
2887
 
                possible_transports=possible_transports)
 
2805
            self._activate_fallback_location(url)
2888
2806
 
2889
2807
    def __init__(self, *args, **kwargs):
2890
2808
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2897
2815
        self._last_revision_info_cache = None
2898
2816
        self._reference_info = None
2899
2817
 
 
2818
    def _last_revision_info(self):
 
2819
        revision_string = self._transport.get_bytes('last-revision')
 
2820
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
 
2821
        revision_id = cache_utf8.get_cached_utf8(revision_id)
 
2822
        revno = int(revno)
 
2823
        return revno, revision_id
 
2824
 
 
2825
    def _write_last_revision_info(self, revno, revision_id):
 
2826
        """Simply write out the revision id, with no checks.
 
2827
 
 
2828
        Use set_last_revision_info to perform this safely.
 
2829
 
 
2830
        Does not update the revision_history cache.
 
2831
        Intended to be called by set_last_revision_info and
 
2832
        _write_revision_history.
 
2833
        """
 
2834
        revision_id = _mod_revision.ensure_null(revision_id)
 
2835
        out_string = '%d %s\n' % (revno, revision_id)
 
2836
        self._transport.put_bytes('last-revision', out_string,
 
2837
            mode=self.bzrdir._get_file_mode())
 
2838
 
 
2839
    @needs_write_lock
 
2840
    def set_last_revision_info(self, revno, revision_id):
 
2841
        revision_id = _mod_revision.ensure_null(revision_id)
 
2842
        old_revno, old_revid = self.last_revision_info()
 
2843
        if self._get_append_revisions_only():
 
2844
            self._check_history_violation(revision_id)
 
2845
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
 
2846
        self._write_last_revision_info(revno, revision_id)
 
2847
        self._clear_cached_state()
 
2848
        self._last_revision_info_cache = revno, revision_id
 
2849
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2850
 
 
2851
    def _synchronize_history(self, destination, revision_id):
 
2852
        """Synchronize last revision and revision history between branches.
 
2853
 
 
2854
        :see: Branch._synchronize_history
 
2855
        """
 
2856
        # XXX: The base Branch has a fast implementation of this method based
 
2857
        # on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
 
2858
        # that uses set_revision_history.  This class inherits from BzrBranch5,
 
2859
        # but wants the fast implementation, so it calls
 
2860
        # Branch._synchronize_history directly.
 
2861
        Branch._synchronize_history(self, destination, revision_id)
 
2862
 
2900
2863
    def _check_history_violation(self, revision_id):
2901
 
        current_revid = self.last_revision()
2902
 
        last_revision = _mod_revision.ensure_null(current_revid)
 
2864
        last_revision = _mod_revision.ensure_null(self.last_revision())
2903
2865
        if _mod_revision.is_null(last_revision):
2904
2866
            return
2905
 
        graph = self.repository.get_graph()
2906
 
        for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2907
 
            if lh_ancestor == current_revid:
2908
 
                return
2909
 
        raise errors.AppendRevisionsOnlyViolation(self.user_url)
 
2867
        if last_revision not in self._lefthand_history(revision_id):
 
2868
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
2910
2869
 
2911
2870
    def _gen_revision_history(self):
2912
2871
        """Generate the revision history from last revision
2915
2874
        self._extend_partial_history(stop_index=last_revno-1)
2916
2875
        return list(reversed(self._partial_revision_history_cache))
2917
2876
 
 
2877
    def _write_revision_history(self, history):
 
2878
        """Factored out of set_revision_history.
 
2879
 
 
2880
        This performs the actual writing to disk, with format-specific checks.
 
2881
        It is intended to be called by BzrBranch5.set_revision_history.
 
2882
        """
 
2883
        if len(history) == 0:
 
2884
            last_revision = 'null:'
 
2885
        else:
 
2886
            if history != self._lefthand_history(history[-1]):
 
2887
                raise errors.NotLefthandHistory(history)
 
2888
            last_revision = history[-1]
 
2889
        if self._get_append_revisions_only():
 
2890
            self._check_history_violation(last_revision)
 
2891
        self._write_last_revision_info(len(history), last_revision)
 
2892
 
2918
2893
    @needs_write_lock
2919
2894
    def _set_parent_location(self, url):
2920
2895
        """Set the parent branch"""
3006
2981
 
3007
2982
    def set_bound_location(self, location):
3008
2983
        """See Branch.set_push_location."""
3009
 
        self._master_branch_cache = None
3010
2984
        result = None
3011
 
        conf = self.get_config_stack()
 
2985
        config = self.get_config()
3012
2986
        if location is None:
3013
 
            if not conf.get('bound'):
 
2987
            if config.get_user_option('bound') != 'True':
3014
2988
                return False
3015
2989
            else:
3016
 
                conf.set('bound', 'False')
 
2990
                config.set_user_option('bound', 'False', warn_masked=True)
3017
2991
                return True
3018
2992
        else:
3019
2993
            self._set_config_location('bound_location', location,
3020
 
                                      config=conf)
3021
 
            conf.set('bound', 'True')
 
2994
                                      config=config)
 
2995
            config.set_user_option('bound', 'True', warn_masked=True)
3022
2996
        return True
3023
2997
 
3024
2998
    def _get_bound_location(self, bound):
3025
2999
        """Return the bound location in the config file.
3026
3000
 
3027
3001
        Return None if the bound parameter does not match"""
3028
 
        conf = self.get_config_stack()
3029
 
        if conf.get('bound') != bound:
 
3002
        config = self.get_config()
 
3003
        config_bound = (config.get_user_option('bound') == 'True')
 
3004
        if config_bound != bound:
3030
3005
            return None
3031
 
        return self._get_config_location('bound_location', config=conf)
 
3006
        return self._get_config_location('bound_location', config=config)
3032
3007
 
3033
3008
    def get_bound_location(self):
3034
 
        """See Branch.get_bound_location."""
 
3009
        """See Branch.set_push_location."""
3035
3010
        return self._get_bound_location(True)
3036
3011
 
3037
3012
    def get_old_bound_location(self):
3042
3017
        # you can always ask for the URL; but you might not be able to use it
3043
3018
        # if the repo can't support stacking.
3044
3019
        ## self._check_stackable_repo()
3045
 
        # stacked_on_location is only ever defined in branch.conf, so don't
3046
 
        # waste effort reading the whole stack of config files.
3047
 
        conf = _mod_config.BranchOnlyStack(self)
3048
 
        stacked_url = self._get_config_location('stacked_on_location',
3049
 
                                                config=conf)
 
3020
        stacked_url = self._get_config_location('stacked_on_location')
3050
3021
        if stacked_url is None:
3051
3022
            raise errors.NotStacked(self)
3052
 
        return stacked_url.encode('utf-8')
 
3023
        return stacked_url
 
3024
 
 
3025
    def _get_append_revisions_only(self):
 
3026
        return self.get_config(
 
3027
            ).get_user_option_as_bool('append_revisions_only')
 
3028
 
 
3029
    @needs_write_lock
 
3030
    def generate_revision_history(self, revision_id, last_rev=None,
 
3031
                                  other_branch=None):
 
3032
        """See BzrBranch5.generate_revision_history"""
 
3033
        history = self._lefthand_history(revision_id, last_rev, other_branch)
 
3034
        revno = len(history)
 
3035
        self.set_last_revision_info(revno, revision_id)
3053
3036
 
3054
3037
    @needs_read_lock
3055
3038
    def get_rev_id(self, revno, history=None):
3080
3063
        try:
3081
3064
            index = self._partial_revision_history_cache.index(revision_id)
3082
3065
        except ValueError:
3083
 
            try:
3084
 
                self._extend_partial_history(stop_revision=revision_id)
3085
 
            except errors.RevisionNotPresent, e:
3086
 
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
 
3066
            self._extend_partial_history(stop_revision=revision_id)
3087
3067
            index = len(self._partial_revision_history_cache) - 1
3088
 
            if index < 0:
3089
 
                raise errors.NoSuchRevision(self, revision_id)
3090
3068
            if self._partial_revision_history_cache[index] != revision_id:
3091
3069
                raise errors.NoSuchRevision(self, revision_id)
3092
3070
        return self.revno() - index
3144
3122
    :ivar local_branch: target branch if there is a Master, else None
3145
3123
    :ivar target_branch: Target/destination branch object. (write locked)
3146
3124
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
3147
 
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3148
3125
    """
3149
3126
 
3150
3127
    @deprecated_method(deprecated_in((2, 3, 0)))
3156
3133
        return self.new_revno - self.old_revno
3157
3134
 
3158
3135
    def report(self, to_file):
3159
 
        tag_conflicts = getattr(self, "tag_conflicts", None)
3160
 
        tag_updates = getattr(self, "tag_updates", None)
3161
3136
        if not is_quiet():
3162
 
            if self.old_revid != self.new_revid:
 
3137
            if self.old_revid == self.new_revid:
 
3138
                to_file.write('No revisions to pull.\n')
 
3139
            else:
3163
3140
                to_file.write('Now on revision %d.\n' % self.new_revno)
3164
 
            if tag_updates:
3165
 
                to_file.write('%d tag(s) updated.\n' % len(tag_updates))
3166
 
            if self.old_revid == self.new_revid and not tag_updates:
3167
 
                if not tag_conflicts:
3168
 
                    to_file.write('No revisions or tags to pull.\n')
3169
 
                else:
3170
 
                    to_file.write('No revisions to pull.\n')
3171
3141
        self._show_tag_conficts(to_file)
3172
3142
 
3173
3143
 
3199
3169
        return self.new_revno - self.old_revno
3200
3170
 
3201
3171
    def report(self, to_file):
3202
 
        # TODO: This function gets passed a to_file, but then
3203
 
        # ignores it and calls note() instead. This is also
3204
 
        # inconsistent with PullResult(), which writes to stdout.
3205
 
        # -- JRV20110901, bug #838853
3206
 
        tag_conflicts = getattr(self, "tag_conflicts", None)
3207
 
        tag_updates = getattr(self, "tag_updates", None)
3208
 
        if not is_quiet():
3209
 
            if self.old_revid != self.new_revid:
3210
 
                note(gettext('Pushed up to revision %d.') % self.new_revno)
3211
 
            if tag_updates:
3212
 
                note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
3213
 
            if self.old_revid == self.new_revid and not tag_updates:
3214
 
                if not tag_conflicts:
3215
 
                    note(gettext('No new revisions or tags to push.'))
3216
 
                else:
3217
 
                    note(gettext('No new revisions to push.'))
 
3172
        """Write a human-readable description of the result."""
 
3173
        if self.old_revid == self.new_revid:
 
3174
            note('No new revisions to push.')
 
3175
        else:
 
3176
            note('Pushed up to revision %d.' % self.new_revno)
3218
3177
        self._show_tag_conficts(to_file)
3219
3178
 
3220
3179
 
3234
3193
        :param verbose: Requests more detailed display of what was checked,
3235
3194
            if any.
3236
3195
        """
3237
 
        note(gettext('checked branch {0} format {1}').format(
3238
 
                                self.branch.user_url, self.branch._format))
 
3196
        note('checked branch %s format %s', self.branch.user_url,
 
3197
            self.branch._format)
3239
3198
        for error in self.errors:
3240
 
            note(gettext('found error:%s'), error)
 
3199
            note('found error:%s', error)
3241
3200
 
3242
3201
 
3243
3202
class Converter5to6(object):
3250
3209
 
3251
3210
        # Copy source data into target
3252
3211
        new_branch._write_last_revision_info(*branch.last_revision_info())
3253
 
        new_branch.lock_write()
3254
 
        try:
3255
 
            new_branch.set_parent(branch.get_parent())
3256
 
            new_branch.set_bound_location(branch.get_bound_location())
3257
 
            new_branch.set_push_location(branch.get_push_location())
3258
 
        finally:
3259
 
            new_branch.unlock()
 
3212
        new_branch.set_parent(branch.get_parent())
 
3213
        new_branch.set_bound_location(branch.get_bound_location())
 
3214
        new_branch.set_push_location(branch.get_push_location())
3260
3215
 
3261
3216
        # New branch has no tags by default
3262
3217
        new_branch.tags._set_tag_dict({})
3263
3218
 
3264
3219
        # Copying done; now update target format
3265
3220
        new_branch._transport.put_bytes('format',
3266
 
            format.as_string(),
 
3221
            format.get_format_string(),
3267
3222
            mode=new_branch.bzrdir._get_file_mode())
3268
3223
 
3269
3224
        # Clean up old files
3270
3225
        new_branch._transport.delete('revision-history')
3271
 
        branch.lock_write()
3272
3226
        try:
3273
 
            try:
3274
 
                branch.set_parent(None)
3275
 
            except errors.NoSuchFile:
3276
 
                pass
3277
 
            branch.set_bound_location(None)
3278
 
        finally:
3279
 
            branch.unlock()
 
3227
            branch.set_parent(None)
 
3228
        except errors.NoSuchFile:
 
3229
            pass
 
3230
        branch.set_bound_location(None)
3280
3231
 
3281
3232
 
3282
3233
class Converter6to7(object):
3286
3237
        format = BzrBranchFormat7()
3287
3238
        branch._set_config_location('stacked_on_location', '')
3288
3239
        # update target format
3289
 
        branch._transport.put_bytes('format', format.as_string())
 
3240
        branch._transport.put_bytes('format', format.get_format_string())
3290
3241
 
3291
3242
 
3292
3243
class Converter7to8(object):
3293
 
    """Perform an in-place upgrade of format 7 to format 8"""
 
3244
    """Perform an in-place upgrade of format 6 to format 7"""
3294
3245
 
3295
3246
    def convert(self, branch):
3296
3247
        format = BzrBranchFormat8()
3297
3248
        branch._transport.put_bytes('references', '')
3298
3249
        # update target format
3299
 
        branch._transport.put_bytes('format', format.as_string())
 
3250
        branch._transport.put_bytes('format', format.get_format_string())
 
3251
 
 
3252
 
 
3253
def _run_with_write_locked_target(target, callable, *args, **kwargs):
 
3254
    """Run ``callable(*args, **kwargs)``, write-locking target for the
 
3255
    duration.
 
3256
 
 
3257
    _run_with_write_locked_target will attempt to release the lock it acquires.
 
3258
 
 
3259
    If an exception is raised by callable, then that exception *will* be
 
3260
    propagated, even if the unlock attempt raises its own error.  Thus
 
3261
    _run_with_write_locked_target should be preferred to simply doing::
 
3262
 
 
3263
        target.lock_write()
 
3264
        try:
 
3265
            return callable(*args, **kwargs)
 
3266
        finally:
 
3267
            target.unlock()
 
3268
 
 
3269
    """
 
3270
    # This is very similar to bzrlib.decorators.needs_write_lock.  Perhaps they
 
3271
    # should share code?
 
3272
    target.lock_write()
 
3273
    try:
 
3274
        result = callable(*args, **kwargs)
 
3275
    except:
 
3276
        exc_info = sys.exc_info()
 
3277
        try:
 
3278
            target.unlock()
 
3279
        finally:
 
3280
            raise exc_info[0], exc_info[1], exc_info[2]
 
3281
    else:
 
3282
        target.unlock()
 
3283
        return result
3300
3284
 
3301
3285
 
3302
3286
class InterBranch(InterObject):
3332
3316
        raise NotImplementedError(self.pull)
3333
3317
 
3334
3318
    @needs_write_lock
3335
 
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
3319
    def update_revisions(self, stop_revision=None, overwrite=False,
 
3320
                         graph=None, fetch_tags=True):
 
3321
        """Pull in new perfect-fit revisions.
 
3322
 
 
3323
        :param stop_revision: Updated until the given revision
 
3324
        :param overwrite: Always set the branch pointer, rather than checking
 
3325
            to see if it is a proper descendant.
 
3326
        :param graph: A Graph object that can be used to query history
 
3327
            information. This can be None.
 
3328
        :param fetch_tags: Flag that specifies if tags from source should be
 
3329
            fetched too.
 
3330
        :return: None
 
3331
        """
 
3332
        raise NotImplementedError(self.update_revisions)
 
3333
 
 
3334
    @needs_write_lock
 
3335
    def push(self, overwrite=False, stop_revision=None,
3336
3336
             _override_hook_source_branch=None):
3337
3337
        """Mirror the source branch into the target branch.
3338
3338
 
3349
3349
        """
3350
3350
        raise NotImplementedError(self.copy_content_into)
3351
3351
 
3352
 
    @needs_write_lock
3353
 
    def fetch(self, stop_revision=None, limit=None):
3354
 
        """Fetch revisions.
3355
 
 
3356
 
        :param stop_revision: Last revision to fetch
3357
 
        :param limit: Optional rough limit of revisions to fetch
3358
 
        """
3359
 
        raise NotImplementedError(self.fetch)
3360
 
 
3361
3352
 
3362
3353
class GenericInterBranch(InterBranch):
3363
3354
    """InterBranch implementation that uses public Branch functions."""
3369
3360
 
3370
3361
    @classmethod
3371
3362
    def _get_branch_formats_to_test(klass):
3372
 
        return [(format_registry.get_default(), format_registry.get_default())]
 
3363
        return [(BranchFormat._default_format, BranchFormat._default_format)]
3373
3364
 
3374
3365
    @classmethod
3375
3366
    def unwrap_format(klass, format):
3398
3389
            self.source.tags.merge_to(self.target.tags)
3399
3390
 
3400
3391
    @needs_write_lock
3401
 
    def fetch(self, stop_revision=None, limit=None):
3402
 
        if self.target.base == self.source.base:
3403
 
            return (0, [])
3404
 
        self.source.lock_read()
3405
 
        try:
3406
 
            fetch_spec_factory = fetch.FetchSpecFactory()
3407
 
            fetch_spec_factory.source_branch = self.source
3408
 
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3409
 
            fetch_spec_factory.source_repo = self.source.repository
3410
 
            fetch_spec_factory.target_repo = self.target.repository
3411
 
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3412
 
            fetch_spec_factory.limit = limit
3413
 
            fetch_spec = fetch_spec_factory.make_fetch_spec()
3414
 
            return self.target.repository.fetch(self.source.repository,
3415
 
                fetch_spec=fetch_spec)
3416
 
        finally:
3417
 
            self.source.unlock()
3418
 
 
3419
 
    @needs_write_lock
3420
 
    def _update_revisions(self, stop_revision=None, overwrite=False,
3421
 
            graph=None):
 
3392
    def update_revisions(self, stop_revision=None, overwrite=False,
 
3393
        graph=None, fetch_tags=True):
 
3394
        """See InterBranch.update_revisions()."""
3422
3395
        other_revno, other_last_revision = self.source.last_revision_info()
3423
3396
        stop_revno = None # unknown
3424
3397
        if stop_revision is None:
3435
3408
        # case of having something to pull, and so that the check for
3436
3409
        # already merged can operate on the just fetched graph, which will
3437
3410
        # be cached in memory.
3438
 
        self.fetch(stop_revision=stop_revision)
 
3411
        if fetch_tags:
 
3412
            fetch_spec_factory = fetch.FetchSpecFactory()
 
3413
            fetch_spec_factory.source_branch = self.source
 
3414
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
 
3415
            fetch_spec_factory.source_repo = self.source.repository
 
3416
            fetch_spec_factory.target_repo = self.target.repository
 
3417
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
 
3418
            fetch_spec = fetch_spec_factory.make_fetch_spec()
 
3419
        else:
 
3420
            fetch_spec = _mod_graph.NotInOtherForRevs(self.target.repository,
 
3421
                self.source.repository, revision_ids=[stop_revision]).execute()
 
3422
        self.target.fetch(self.source, fetch_spec=fetch_spec)
3439
3423
        # Check to see if one is an ancestor of the other
3440
3424
        if not overwrite:
3441
3425
            if graph is None:
3469
3453
        if local and not bound_location:
3470
3454
            raise errors.LocalRequiresBoundBranch()
3471
3455
        master_branch = None
3472
 
        source_is_master = False
3473
 
        if bound_location:
3474
 
            # bound_location comes from a config file, some care has to be
3475
 
            # taken to relate it to source.user_url
3476
 
            normalized = urlutils.normalize_url(bound_location)
3477
 
            try:
3478
 
                relpath = self.source.user_transport.relpath(normalized)
3479
 
                source_is_master = (relpath == '')
3480
 
            except (errors.PathNotChild, errors.InvalidURL):
3481
 
                source_is_master = False
 
3456
        source_is_master = (self.source.user_url == bound_location)
3482
3457
        if not local and bound_location and not source_is_master:
3483
3458
            # not pulling from master, so we need to update master.
3484
3459
            master_branch = self.target.get_master_branch(possible_transports)
3497
3472
            if master_branch:
3498
3473
                master_branch.unlock()
3499
3474
 
3500
 
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
3475
    def push(self, overwrite=False, stop_revision=None,
3501
3476
             _override_hook_source_branch=None):
3502
3477
        """See InterBranch.push.
3503
3478
 
3504
3479
        This is the basic concrete implementation of push()
3505
3480
 
3506
 
        :param _override_hook_source_branch: If specified, run the hooks
3507
 
            passing this Branch as the source, rather than self.  This is for
3508
 
            use of RemoteBranch, where push is delegated to the underlying
3509
 
            vfs-based Branch.
 
3481
        :param _override_hook_source_branch: If specified, run
 
3482
        the hooks passing this Branch as the source, rather than self.
 
3483
        This is for use of RemoteBranch, where push is delegated to the
 
3484
        underlying vfs-based Branch.
3510
3485
        """
3511
 
        if lossy:
3512
 
            raise errors.LossyPushToSameVCS(self.source, self.target)
3513
3486
        # TODO: Public option to disable running hooks - should be trivial but
3514
3487
        # needs tests.
3515
 
 
3516
 
        op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3517
 
        op.add_cleanup(self.source.lock_read().unlock)
3518
 
        op.add_cleanup(self.target.lock_write().unlock)
3519
 
        return op.run(overwrite, stop_revision,
3520
 
            _override_hook_source_branch=_override_hook_source_branch)
3521
 
 
3522
 
    def _basic_push(self, overwrite, stop_revision):
3523
 
        """Basic implementation of push without bound branches or hooks.
3524
 
 
3525
 
        Must be called with source read locked and target write locked.
3526
 
        """
3527
 
        result = BranchPushResult()
3528
 
        result.source_branch = self.source
3529
 
        result.target_branch = self.target
3530
 
        result.old_revno, result.old_revid = self.target.last_revision_info()
3531
 
        self.source.update_references(self.target)
3532
 
        if result.old_revid != stop_revision:
3533
 
            # We assume that during 'push' this repository is closer than
3534
 
            # the target.
3535
 
            graph = self.source.repository.get_graph(self.target.repository)
3536
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3537
 
                    graph=graph)
3538
 
        if self.source._push_should_merge_tags():
3539
 
            result.tag_updates, result.tag_conflicts = (
3540
 
                self.source.tags.merge_to(self.target.tags, overwrite))
3541
 
        result.new_revno, result.new_revid = self.target.last_revision_info()
3542
 
        return result
3543
 
 
3544
 
    def _push_with_bound_branches(self, operation, overwrite, stop_revision,
 
3488
        self.source.lock_read()
 
3489
        try:
 
3490
            return _run_with_write_locked_target(
 
3491
                self.target, self._push_with_bound_branches, overwrite,
 
3492
                stop_revision,
 
3493
                _override_hook_source_branch=_override_hook_source_branch)
 
3494
        finally:
 
3495
            self.source.unlock()
 
3496
 
 
3497
    def _push_with_bound_branches(self, overwrite, stop_revision,
3545
3498
            _override_hook_source_branch=None):
3546
3499
        """Push from source into target, and into target's master if any.
3547
3500
        """
3559
3512
            # be bound to itself? -- mbp 20070507
3560
3513
            master_branch = self.target.get_master_branch()
3561
3514
            master_branch.lock_write()
3562
 
            operation.add_cleanup(master_branch.unlock)
3563
 
            # push into the master from the source branch.
3564
 
            master_inter = InterBranch.get(self.source, master_branch)
3565
 
            master_inter._basic_push(overwrite, stop_revision)
3566
 
            # and push into the target branch from the source. Note that
3567
 
            # we push from the source branch again, because it's considered
3568
 
            # the highest bandwidth repository.
3569
 
            result = self._basic_push(overwrite, stop_revision)
3570
 
            result.master_branch = master_branch
3571
 
            result.local_branch = self.target
 
3515
            try:
 
3516
                # push into the master from the source branch.
 
3517
                self.source._basic_push(master_branch, overwrite, stop_revision)
 
3518
                # and push into the target branch from the source. Note that we
 
3519
                # push from the source branch again, because it's considered the
 
3520
                # highest bandwidth repository.
 
3521
                result = self.source._basic_push(self.target, overwrite,
 
3522
                    stop_revision)
 
3523
                result.master_branch = master_branch
 
3524
                result.local_branch = self.target
 
3525
                _run_hooks()
 
3526
                return result
 
3527
            finally:
 
3528
                master_branch.unlock()
3572
3529
        else:
3573
 
            master_branch = None
3574
3530
            # no master branch
3575
 
            result = self._basic_push(overwrite, stop_revision)
 
3531
            result = self.source._basic_push(self.target, overwrite,
 
3532
                stop_revision)
3576
3533
            # TODO: Why set master_branch and local_branch if there's no
3577
3534
            # binding?  Maybe cleaner to just leave them unset? -- mbp
3578
3535
            # 20070504
3579
3536
            result.master_branch = self.target
3580
3537
            result.local_branch = None
3581
 
        _run_hooks()
3582
 
        return result
 
3538
            _run_hooks()
 
3539
            return result
3583
3540
 
3584
3541
    def _pull(self, overwrite=False, stop_revision=None,
3585
3542
             possible_transports=None, _hook_master=None, run_hooks=True,
3595
3552
        :param run_hooks: Private parameter - if false, this branch
3596
3553
            is being called because it's the master of the primary branch,
3597
3554
            so it should not run its hooks.
3598
 
            is being called because it's the master of the primary branch,
3599
 
            so it should not run its hooks.
3600
3555
        :param _override_hook_target: Private parameter - set the branch to be
3601
3556
            supplied as the target_branch to pull hooks.
3602
3557
        :param local: Only update the local branch, and not the bound branch.
3621
3576
            # -- JRV20090506
3622
3577
            result.old_revno, result.old_revid = \
3623
3578
                self.target.last_revision_info()
3624
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3625
 
                graph=graph)
 
3579
            self.target.update_revisions(self.source, stop_revision,
 
3580
                overwrite=overwrite, graph=graph)
3626
3581
            # TODO: The old revid should be specified when merging tags, 
3627
3582
            # so a tags implementation that versions tags can only 
3628
3583
            # pull in the most recent changes. -- JRV20090506
3629
 
            result.tag_updates, result.tag_conflicts = (
3630
 
                self.source.tags.merge_to(self.target.tags, overwrite,
3631
 
                    ignore_master=not merge_tags_to_master))
 
3584
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3585
                overwrite, ignore_master=not merge_tags_to_master)
3632
3586
            result.new_revno, result.new_revid = self.target.last_revision_info()
3633
3587
            if _hook_master:
3634
3588
                result.master_branch = _hook_master