~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Andrew Bennetts
  • Date: 2010-10-13 00:26:41 UTC
  • mto: This revision was merged to the branch mainline in revision 5498.
  • Revision ID: andrew.bennetts@canonical.com-20101013002641-9tlh9k89mlj1666m
Keep docs-plain working.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
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
        lockdir,
 
32
        lockable_files,
 
33
        remote,
 
34
        repository,
 
35
        revision as _mod_revision,
 
36
        rio,
 
37
        symbol_versioning,
 
38
        transport,
 
39
        tsort,
 
40
        ui,
 
41
        urlutils,
 
42
        )
 
43
from bzrlib.config import BranchConfig, TransportConfig
 
44
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
 
45
from bzrlib.tag import (
 
46
    BasicTags,
 
47
    DisabledTags,
47
48
    )
48
 
from bzrlib.i18n import gettext, ngettext
49
49
""")
50
50
 
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
 
    controldir,
57
 
    )
58
 
from bzrlib.decorators import (
59
 
    needs_read_lock,
60
 
    needs_write_lock,
61
 
    only_raises,
62
 
    )
63
 
from bzrlib.hooks import Hooks
 
51
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
 
52
from bzrlib.hooks import HookPoint, Hooks
64
53
from bzrlib.inter import InterObject
65
54
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
66
55
from bzrlib import registry
71
60
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
72
61
 
73
62
 
 
63
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
 
64
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
 
65
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
 
66
 
 
67
 
74
68
class Branch(controldir.ControlComponent):
75
69
    """Branch holding a history of revisions.
76
70
 
77
71
    :ivar base:
78
72
        Base directory/url of the branch; using control_url and
79
73
        control_transport is more standardized.
80
 
    :ivar hooks: An instance of BranchHooks.
81
 
    :ivar _master_branch_cache: cached result of get_master_branch, see
82
 
        _clear_cached_state.
 
74
 
 
75
    hooks: An instance of BranchHooks.
83
76
    """
84
77
    # this is really an instance variable - FIXME move it there
85
78
    # - RBC 20060112
93
86
    def user_transport(self):
94
87
        return self.bzrdir.user_transport
95
88
 
96
 
    def __init__(self, possible_transports=None):
 
89
    def __init__(self, *ignored, **ignored_too):
97
90
        self.tags = self._format.make_tags(self)
98
91
        self._revision_history_cache = None
99
92
        self._revision_id_to_revno_cache = None
100
93
        self._partial_revision_id_to_revno_cache = {}
101
94
        self._partial_revision_history_cache = []
102
 
        self._tags_bytes = None
103
95
        self._last_revision_info_cache = None
104
 
        self._master_branch_cache = None
105
96
        self._merge_sorted_revisions_cache = None
106
 
        self._open_hook(possible_transports)
 
97
        self._open_hook()
107
98
        hooks = Branch.hooks['open']
108
99
        for hook in hooks:
109
100
            hook(self)
110
101
 
111
 
    def _open_hook(self, possible_transports):
 
102
    def _open_hook(self):
112
103
        """Called by init to allow simpler extension of the base class."""
113
104
 
114
 
    def _activate_fallback_location(self, url, possible_transports):
 
105
    def _activate_fallback_location(self, url):
115
106
        """Activate the branch/repository from url as a fallback repository."""
116
 
        for existing_fallback_repo in self.repository._fallback_repositories:
117
 
            if existing_fallback_repo.user_url == url:
118
 
                # This fallback is already configured.  This probably only
119
 
                # happens because ControlDir.sprout is a horrible mess.  To avoid
120
 
                # confusing _unstack we don't add this a second time.
121
 
                mutter('duplicate activation of fallback %r on %r', url, self)
122
 
                return
123
 
        repo = self._get_fallback_repository(url, possible_transports)
 
107
        repo = self._get_fallback_repository(url)
124
108
        if repo.has_same_location(self.repository):
125
109
            raise errors.UnstackableLocationError(self.user_url, url)
126
110
        self.repository.add_fallback_repository(repo)
180
164
        For instance, if the branch is at URL/.bzr/branch,
181
165
        Branch.open(URL) -> a Branch instance.
182
166
        """
183
 
        control = controldir.ControlDir.open(base, _unsupported,
 
167
        control = bzrdir.BzrDir.open(base, _unsupported,
184
168
                                     possible_transports=possible_transports)
185
 
        return control.open_branch(unsupported=_unsupported,
186
 
            possible_transports=possible_transports)
 
169
        return control.open_branch(unsupported=_unsupported)
187
170
 
188
171
    @staticmethod
189
 
    def open_from_transport(transport, name=None, _unsupported=False,
190
 
            possible_transports=None):
 
172
    def open_from_transport(transport, name=None, _unsupported=False):
191
173
        """Open the branch rooted at transport"""
192
 
        control = controldir.ControlDir.open_from_transport(transport, _unsupported)
193
 
        return control.open_branch(name=name, unsupported=_unsupported,
194
 
            possible_transports=possible_transports)
 
174
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
 
175
        return control.open_branch(name=name, unsupported=_unsupported)
195
176
 
196
177
    @staticmethod
197
178
    def open_containing(url, possible_transports=None):
205
186
        format, UnknownFormatError or UnsupportedFormatError are raised.
206
187
        If there is one, it is returned, along with the unused portion of url.
207
188
        """
208
 
        control, relpath = controldir.ControlDir.open_containing(url,
 
189
        control, relpath = bzrdir.BzrDir.open_containing(url,
209
190
                                                         possible_transports)
210
 
        branch = control.open_branch(possible_transports=possible_transports)
211
 
        return (branch, relpath)
 
191
        return control.open_branch(), relpath
212
192
 
213
193
    def _push_should_merge_tags(self):
214
194
        """Should _basic_push merge this branch's tags into the target?
226
206
 
227
207
        :return: A bzrlib.config.BranchConfig.
228
208
        """
229
 
        return _mod_config.BranchConfig(self)
230
 
 
231
 
    def get_config_stack(self):
232
 
        """Get a bzrlib.config.BranchStack for this Branch.
233
 
 
234
 
        This can then be used to get and set configuration options for the
235
 
        branch.
236
 
 
237
 
        :return: A bzrlib.config.BranchStack.
238
 
        """
239
 
        return _mod_config.BranchStack(self)
 
209
        return BranchConfig(self)
240
210
 
241
211
    def _get_config(self):
242
212
        """Get the concrete config for just the config in this branch.
250
220
        """
251
221
        raise NotImplementedError(self._get_config)
252
222
 
253
 
    def _get_fallback_repository(self, url, possible_transports):
 
223
    def _get_fallback_repository(self, url):
254
224
        """Get the repository we fallback to at url."""
255
225
        url = urlutils.join(self.base, url)
256
 
        a_branch = Branch.open(url, possible_transports=possible_transports)
 
226
        a_branch = Branch.open(url,
 
227
            possible_transports=[self.bzrdir.root_transport])
257
228
        return a_branch.repository
258
229
 
259
 
    @needs_read_lock
260
230
    def _get_tags_bytes(self):
261
231
        """Get the bytes of a serialised tags dict.
262
232
 
269
239
        :return: The bytes of the tags file.
270
240
        :seealso: Branch._set_tags_bytes.
271
241
        """
272
 
        if self._tags_bytes is None:
273
 
            self._tags_bytes = self._transport.get_bytes('tags')
274
 
        return self._tags_bytes
 
242
        return self._transport.get_bytes('tags')
275
243
 
276
244
    def _get_nick(self, local=False, possible_transports=None):
277
245
        config = self.get_config()
468
436
            after. If None, the rest of history is included.
469
437
        :param stop_rule: if stop_revision_id is not None, the precise rule
470
438
            to use for termination:
471
 
 
472
439
            * 'exclude' - leave the stop revision out of the result (default)
473
440
            * 'include' - the stop revision is the last item in the result
474
441
            * 'with-merges' - include the stop revision and all of its
476
443
            * 'with-merges-without-common-ancestry' - filter out revisions 
477
444
              that are in both ancestries
478
445
        :param direction: either 'reverse' or 'forward':
479
 
 
480
446
            * reverse means return the start_revision_id first, i.e.
481
447
              start at the most recent revision and go backwards in history
482
448
            * forward returns tuples in the opposite order to reverse.
526
492
        rev_iter = iter(merge_sorted_revisions)
527
493
        if start_revision_id is not None:
528
494
            for node in rev_iter:
529
 
                rev_id = node.key
 
495
                rev_id = node.key[-1]
530
496
                if rev_id != start_revision_id:
531
497
                    continue
532
498
                else:
533
499
                    # The decision to include the start or not
534
500
                    # depends on the stop_rule if a stop is provided
535
501
                    # so pop this node back into the iterator
536
 
                    rev_iter = itertools.chain(iter([node]), rev_iter)
 
502
                    rev_iter = chain(iter([node]), rev_iter)
537
503
                    break
538
504
        if stop_revision_id is None:
539
505
            # Yield everything
540
506
            for node in rev_iter:
541
 
                rev_id = node.key
 
507
                rev_id = node.key[-1]
542
508
                yield (rev_id, node.merge_depth, node.revno,
543
509
                       node.end_of_merge)
544
510
        elif stop_rule == 'exclude':
545
511
            for node in rev_iter:
546
 
                rev_id = node.key
 
512
                rev_id = node.key[-1]
547
513
                if rev_id == stop_revision_id:
548
514
                    return
549
515
                yield (rev_id, node.merge_depth, node.revno,
550
516
                       node.end_of_merge)
551
517
        elif stop_rule == 'include':
552
518
            for node in rev_iter:
553
 
                rev_id = node.key
 
519
                rev_id = node.key[-1]
554
520
                yield (rev_id, node.merge_depth, node.revno,
555
521
                       node.end_of_merge)
556
522
                if rev_id == stop_revision_id:
562
528
            ancestors = graph.find_unique_ancestors(start_revision_id,
563
529
                                                    [stop_revision_id])
564
530
            for node in rev_iter:
565
 
                rev_id = node.key
 
531
                rev_id = node.key[-1]
566
532
                if rev_id not in ancestors:
567
533
                    continue
568
534
                yield (rev_id, node.merge_depth, node.revno,
578
544
            reached_stop_revision_id = False
579
545
            revision_id_whitelist = []
580
546
            for node in rev_iter:
581
 
                rev_id = node.key
 
547
                rev_id = node.key[-1]
582
548
                if rev_id == left_parent:
583
549
                    # reached the left parent after the stop_revision
584
550
                    return
664
630
        """
665
631
        raise errors.UpgradeRequired(self.user_url)
666
632
 
667
 
    def get_append_revisions_only(self):
668
 
        """Whether it is only possible to append revisions to the history.
669
 
        """
670
 
        if not self._format.supports_set_append_revisions_only():
671
 
            return False
672
 
        return self.get_config_stack().get('append_revisions_only')
673
 
 
674
633
    def set_append_revisions_only(self, enabled):
675
634
        if not self._format.supports_set_append_revisions_only():
676
635
            raise errors.UpgradeRequired(self.user_url)
677
 
        self.get_config_stack().set('append_revisions_only', enabled)
 
636
        if enabled:
 
637
            value = 'True'
 
638
        else:
 
639
            value = 'False'
 
640
        self.get_config().set_user_option('append_revisions_only', value,
 
641
            warn_masked=True)
678
642
 
679
643
    def set_reference_info(self, file_id, tree_path, branch_location):
680
644
        """Set the branch location to use for a tree reference."""
685
649
        raise errors.UnsupportedOperation(self.get_reference_info, self)
686
650
 
687
651
    @needs_write_lock
688
 
    def fetch(self, from_branch, last_revision=None, limit=None):
 
652
    def fetch(self, from_branch, last_revision=None, pb=None):
689
653
        """Copy revisions from from_branch into this branch.
690
654
 
691
655
        :param from_branch: Where to copy from.
692
656
        :param last_revision: What revision to stop at (None for at the end
693
657
                              of the branch.
694
 
        :param limit: Optional rough limit of revisions to fetch
 
658
        :param pb: An optional progress bar to use.
695
659
        :return: None
696
660
        """
697
 
        return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
 
661
        if self.base == from_branch.base:
 
662
            return (0, [])
 
663
        if pb is not None:
 
664
            symbol_versioning.warn(
 
665
                symbol_versioning.deprecated_in((1, 14, 0))
 
666
                % "pb parameter to fetch()")
 
667
        from_branch.lock_read()
 
668
        try:
 
669
            if last_revision is None:
 
670
                last_revision = from_branch.last_revision()
 
671
                last_revision = _mod_revision.ensure_null(last_revision)
 
672
            return self.repository.fetch(from_branch.repository,
 
673
                                         revision_id=last_revision,
 
674
                                         pb=pb)
 
675
        finally:
 
676
            from_branch.unlock()
698
677
 
699
678
    def get_bound_location(self):
700
679
        """Return the URL of the branch we are bound to.
709
688
        """
710
689
        raise errors.UpgradeRequired(self.user_url)
711
690
 
712
 
    def get_commit_builder(self, parents, config_stack=None, timestamp=None,
 
691
    def get_commit_builder(self, parents, config=None, timestamp=None,
713
692
                           timezone=None, committer=None, revprops=None,
714
 
                           revision_id=None, lossy=False):
 
693
                           revision_id=None):
715
694
        """Obtain a CommitBuilder for this branch.
716
695
 
717
696
        :param parents: Revision ids of the parents of the new revision.
721
700
        :param committer: Optional committer to set for commit.
722
701
        :param revprops: Optional dictionary of revision properties.
723
702
        :param revision_id: Optional revision id.
724
 
        :param lossy: Whether to discard data that can not be natively
725
 
            represented, when pushing to a foreign VCS 
726
703
        """
727
704
 
728
 
        if config_stack is None:
729
 
            config_stack = self.get_config_stack()
 
705
        if config is None:
 
706
            config = self.get_config()
730
707
 
731
 
        return self.repository.get_commit_builder(self, parents, config_stack,
732
 
            timestamp, timezone, committer, revprops, revision_id,
733
 
            lossy)
 
708
        return self.repository.get_commit_builder(self, parents, config,
 
709
            timestamp, timezone, committer, revprops, revision_id)
734
710
 
735
711
    def get_master_branch(self, possible_transports=None):
736
712
        """Return the branch we are bound to.
739
715
        """
740
716
        return None
741
717
 
742
 
    @deprecated_method(deprecated_in((2, 5, 0)))
743
718
    def get_revision_delta(self, revno):
744
719
        """Return the delta for one revision.
745
720
 
746
721
        The delta is relative to its mainline predecessor, or the
747
722
        empty tree for revision 1.
748
723
        """
749
 
        try:
750
 
            revid = self.get_rev_id(revno)
751
 
        except errors.NoSuchRevision:
 
724
        rh = self.revision_history()
 
725
        if not (1 <= revno <= len(rh)):
752
726
            raise errors.InvalidRevisionNumber(revno)
753
 
        return self.repository.get_revision_delta(revid)
 
727
        return self.repository.get_revision_delta(rh[revno-1])
754
728
 
755
729
    def get_stacked_on_url(self):
756
730
        """Get the URL this branch is stacked against.
765
739
        """Print `file` to stdout."""
766
740
        raise NotImplementedError(self.print_file)
767
741
 
768
 
    @deprecated_method(deprecated_in((2, 4, 0)))
769
742
    def set_revision_history(self, rev_history):
770
 
        """See Branch.set_revision_history."""
771
 
        self._set_revision_history(rev_history)
772
 
 
773
 
    @needs_write_lock
774
 
    def _set_revision_history(self, rev_history):
775
 
        if len(rev_history) == 0:
776
 
            revid = _mod_revision.NULL_REVISION
777
 
        else:
778
 
            revid = rev_history[-1]
779
 
        if rev_history != self._lefthand_history(revid):
780
 
            raise errors.NotLefthandHistory(rev_history)
781
 
        self.set_last_revision_info(len(rev_history), revid)
782
 
        self._cache_revision_history(rev_history)
783
 
        for hook in Branch.hooks['set_rh']:
784
 
            hook(self, rev_history)
785
 
 
786
 
    @needs_write_lock
787
 
    def set_last_revision_info(self, revno, revision_id):
788
 
        """Set the last revision of this branch.
789
 
 
790
 
        The caller is responsible for checking that the revno is correct
791
 
        for this revision id.
792
 
 
793
 
        It may be possible to set the branch last revision to an id not
794
 
        present in the repository.  However, branches can also be
795
 
        configured to check constraints on history, in which case this may not
796
 
        be permitted.
797
 
        """
798
 
        raise NotImplementedError(self.set_last_revision_info)
799
 
 
800
 
    @needs_write_lock
801
 
    def generate_revision_history(self, revision_id, last_rev=None,
802
 
                                  other_branch=None):
803
 
        """See Branch.generate_revision_history"""
804
 
        graph = self.repository.get_graph()
805
 
        (last_revno, last_revid) = self.last_revision_info()
806
 
        known_revision_ids = [
807
 
            (last_revid, last_revno),
808
 
            (_mod_revision.NULL_REVISION, 0),
809
 
            ]
810
 
        if last_rev is not None:
811
 
            if not graph.is_ancestor(last_rev, revision_id):
812
 
                # our previous tip is not merged into stop_revision
813
 
                raise errors.DivergedBranches(self, other_branch)
814
 
        revno = graph.find_distance_to_null(revision_id, known_revision_ids)
815
 
        self.set_last_revision_info(revno, revision_id)
 
743
        raise NotImplementedError(self.set_revision_history)
816
744
 
817
745
    @needs_write_lock
818
746
    def set_parent(self, url):
855
783
                return
856
784
            self._unstack()
857
785
        else:
858
 
            self._activate_fallback_location(url,
859
 
                possible_transports=[self.bzrdir.root_transport])
 
786
            self._activate_fallback_location(url)
860
787
        # write this out after the repository is stacked to avoid setting a
861
788
        # stacked config that doesn't work.
862
789
        self._set_config_location('stacked_on_location', url)
863
790
 
864
791
    def _unstack(self):
865
792
        """Change a branch to be unstacked, copying data as needed.
866
 
 
 
793
        
867
794
        Don't call this directly, use set_stacked_on_url(None).
868
795
        """
869
796
        pb = ui.ui_factory.nested_progress_bar()
870
797
        try:
871
 
            pb.update(gettext("Unstacking"))
 
798
            pb.update("Unstacking")
872
799
            # The basic approach here is to fetch the tip of the branch,
873
800
            # including all available ghosts, from the existing stacked
874
801
            # repository into a new repository object without the fallbacks. 
878
805
            old_repository = self.repository
879
806
            if len(old_repository._fallback_repositories) != 1:
880
807
                raise AssertionError("can't cope with fallback repositories "
881
 
                    "of %r (fallbacks: %r)" % (old_repository,
882
 
                        old_repository._fallback_repositories))
 
808
                    "of %r" % (self.repository,))
883
809
            # Open the new repository object.
884
810
            # Repositories don't offer an interface to remove fallback
885
811
            # repositories today; take the conceptually simpler option and just
888
814
            # stream from one of them to the other.  This does mean doing
889
815
            # separate SSH connection setup, but unstacking is not a
890
816
            # common operation so it's tolerable.
891
 
            new_bzrdir = controldir.ControlDir.open(
892
 
                self.bzrdir.root_transport.base)
 
817
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
893
818
            new_repository = new_bzrdir.find_repository()
894
819
            if new_repository._fallback_repositories:
895
820
                raise AssertionError("didn't expect %r to have "
934
859
                # XXX: If you unstack a branch while it has a working tree
935
860
                # with a pending merge, the pending-merged revisions will no
936
861
                # longer be present.  You can (probably) revert and remerge.
937
 
                try:
938
 
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
939
 
                except errors.TagsNotSupported:
940
 
                    tags_to_fetch = set()
941
 
                fetch_spec = vf_search.NotInOtherForRevs(self.repository,
942
 
                    old_repository, required_ids=[self.last_revision()],
943
 
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
944
 
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
 
862
                #
 
863
                # XXX: This only fetches up to the tip of the repository; it
 
864
                # doesn't bring across any tags.  That's fairly consistent
 
865
                # with how branch works, but perhaps not ideal.
 
866
                self.repository.fetch(old_repository,
 
867
                    revision_id=self.last_revision(),
 
868
                    find_ghosts=True)
945
869
            finally:
946
870
                old_repository.unlock()
947
871
        finally:
952
876
 
953
877
        :seealso: Branch._get_tags_bytes.
954
878
        """
955
 
        op = cleanup.OperationWithCleanups(self._set_tags_bytes_locked)
956
 
        op.add_cleanup(self.lock_write().unlock)
957
 
        return op.run_simple(bytes)
958
 
 
959
 
    def _set_tags_bytes_locked(self, bytes):
960
 
        self._tags_bytes = bytes
961
 
        return self._transport.put_bytes('tags', bytes)
 
879
        return _run_with_write_locked_target(self, self._transport.put_bytes,
 
880
            'tags', bytes)
962
881
 
963
882
    def _cache_revision_history(self, rev_history):
964
883
        """Set the cached revision history to rev_history.
991
910
        self._revision_history_cache = None
992
911
        self._revision_id_to_revno_cache = None
993
912
        self._last_revision_info_cache = None
994
 
        self._master_branch_cache = None
995
913
        self._merge_sorted_revisions_cache = None
996
914
        self._partial_revision_history_cache = []
997
915
        self._partial_revision_id_to_revno_cache = {}
998
 
        self._tags_bytes = None
999
916
 
1000
917
    def _gen_revision_history(self):
1001
918
        """Return sequence of revision hashes on to this branch.
1012
929
        """
1013
930
        raise NotImplementedError(self._gen_revision_history)
1014
931
 
1015
 
    @deprecated_method(deprecated_in((2, 5, 0)))
1016
932
    @needs_read_lock
1017
933
    def revision_history(self):
1018
934
        """Return sequence of revision ids on this branch.
1020
936
        This method will cache the revision history for as long as it is safe to
1021
937
        do so.
1022
938
        """
1023
 
        return self._revision_history()
1024
 
 
1025
 
    def _revision_history(self):
1026
939
        if 'evil' in debug.debug_flags:
1027
940
            mutter_callsite(3, "revision_history scales with history.")
1028
941
        if self._revision_history_cache is not None:
1055
968
        :return: A tuple (revno, revision_id).
1056
969
        """
1057
970
        if self._last_revision_info_cache is None:
1058
 
            self._last_revision_info_cache = self._read_last_revision_info()
 
971
            self._last_revision_info_cache = self._last_revision_info()
1059
972
        return self._last_revision_info_cache
1060
973
 
1061
 
    def _read_last_revision_info(self):
1062
 
        raise NotImplementedError(self._read_last_revision_info)
1063
 
 
1064
 
    @deprecated_method(deprecated_in((2, 4, 0)))
 
974
    def _last_revision_info(self):
 
975
        rh = self.revision_history()
 
976
        revno = len(rh)
 
977
        if revno:
 
978
            return (revno, rh[-1])
 
979
        else:
 
980
            return (0, _mod_revision.NULL_REVISION)
 
981
 
 
982
    @deprecated_method(deprecated_in((1, 6, 0)))
 
983
    def missing_revisions(self, other, stop_revision=None):
 
984
        """Return a list of new revisions that would perfectly fit.
 
985
 
 
986
        If self and other have not diverged, return a list of the revisions
 
987
        present in other, but missing from self.
 
988
        """
 
989
        self_history = self.revision_history()
 
990
        self_len = len(self_history)
 
991
        other_history = other.revision_history()
 
992
        other_len = len(other_history)
 
993
        common_index = min(self_len, other_len) -1
 
994
        if common_index >= 0 and \
 
995
            self_history[common_index] != other_history[common_index]:
 
996
            raise errors.DivergedBranches(self, other)
 
997
 
 
998
        if stop_revision is None:
 
999
            stop_revision = other_len
 
1000
        else:
 
1001
            if stop_revision > other_len:
 
1002
                raise errors.NoSuchRevision(self, stop_revision)
 
1003
        return other_history[self_len:stop_revision]
 
1004
 
 
1005
    def update_revisions(self, other, stop_revision=None, overwrite=False,
 
1006
                         graph=None):
 
1007
        """Pull in new perfect-fit revisions.
 
1008
 
 
1009
        :param other: Another Branch to pull from
 
1010
        :param stop_revision: Updated until the given revision
 
1011
        :param overwrite: Always set the branch pointer, rather than checking
 
1012
            to see if it is a proper descendant.
 
1013
        :param graph: A Graph object that can be used to query history
 
1014
            information. This can be None.
 
1015
        :return: None
 
1016
        """
 
1017
        return InterBranch.get(other, self).update_revisions(stop_revision,
 
1018
            overwrite, graph)
 
1019
 
1065
1020
    def import_last_revision_info(self, source_repo, revno, revid):
1066
1021
        """Set the last revision info, importing from another repo if necessary.
1067
1022
 
 
1023
        This is used by the bound branch code to upload a revision to
 
1024
        the master branch first before updating the tip of the local branch.
 
1025
 
1068
1026
        :param source_repo: Source repository to optionally fetch from
1069
1027
        :param revno: Revision number of the new tip
1070
1028
        :param revid: Revision id of the new tip
1073
1031
            self.repository.fetch(source_repo, revision_id=revid)
1074
1032
        self.set_last_revision_info(revno, revid)
1075
1033
 
1076
 
    def import_last_revision_info_and_tags(self, source, revno, revid,
1077
 
                                           lossy=False):
1078
 
        """Set the last revision info, importing from another repo if necessary.
1079
 
 
1080
 
        This is used by the bound branch code to upload a revision to
1081
 
        the master branch first before updating the tip of the local branch.
1082
 
        Revisions referenced by source's tags are also transferred.
1083
 
 
1084
 
        :param source: Source branch to optionally fetch from
1085
 
        :param revno: Revision number of the new tip
1086
 
        :param revid: Revision id of the new tip
1087
 
        :param lossy: Whether to discard metadata that can not be
1088
 
            natively represented
1089
 
        :return: Tuple with the new revision number and revision id
1090
 
            (should only be different from the arguments when lossy=True)
1091
 
        """
1092
 
        if not self.repository.has_same_location(source.repository):
1093
 
            self.fetch(source, revid)
1094
 
        self.set_last_revision_info(revno, revid)
1095
 
        return (revno, revid)
1096
 
 
1097
1034
    def revision_id_to_revno(self, revision_id):
1098
1035
        """Given a revision id, return its revno"""
1099
1036
        if _mod_revision.is_null(revision_id):
1100
1037
            return 0
1101
 
        history = self._revision_history()
 
1038
        history = self.revision_history()
1102
1039
        try:
1103
1040
            return history.index(revision_id) + 1
1104
1041
        except ValueError:
1131
1068
            stop_revision=stop_revision,
1132
1069
            possible_transports=possible_transports, *args, **kwargs)
1133
1070
 
1134
 
    def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1135
 
            *args, **kwargs):
 
1071
    def push(self, target, overwrite=False, stop_revision=None, *args,
 
1072
        **kwargs):
1136
1073
        """Mirror this branch into target.
1137
1074
 
1138
1075
        This branch is considered to be 'local', having low latency.
1139
1076
        """
1140
1077
        return InterBranch.get(self, target).push(overwrite, stop_revision,
1141
 
            lossy, *args, **kwargs)
 
1078
            *args, **kwargs)
 
1079
 
 
1080
    def lossy_push(self, target, stop_revision=None):
 
1081
        """Push deltas into another branch.
 
1082
 
 
1083
        :note: This does not, like push, retain the revision ids from 
 
1084
            the source branch and will, rather than adding bzr-specific 
 
1085
            metadata, push only those semantics of the revision that can be 
 
1086
            natively represented by this branch' VCS.
 
1087
 
 
1088
        :param target: Target branch
 
1089
        :param stop_revision: Revision to push, defaults to last revision.
 
1090
        :return: BranchPushResult with an extra member revidmap: 
 
1091
            A dictionary mapping revision ids from the target branch 
 
1092
            to new revision ids in the target branch, for each 
 
1093
            revision that was pushed.
 
1094
        """
 
1095
        inter = InterBranch.get(self, target)
 
1096
        lossy_push = getattr(inter, "lossy_push", None)
 
1097
        if lossy_push is None:
 
1098
            raise errors.LossyPushToSameVCS(self, target)
 
1099
        return lossy_push(stop_revision)
1142
1100
 
1143
1101
    def basis_tree(self):
1144
1102
        """Return `Tree` object for last revision."""
1169
1127
    def _set_config_location(self, name, url, config=None,
1170
1128
                             make_relative=False):
1171
1129
        if config is None:
1172
 
            config = self.get_config_stack()
 
1130
            config = self.get_config()
1173
1131
        if url is None:
1174
1132
            url = ''
1175
1133
        elif make_relative:
1176
1134
            url = urlutils.relative_url(self.base, url)
1177
 
        config.set(name, url)
 
1135
        config.set_user_option(name, url, warn_masked=True)
1178
1136
 
1179
1137
    def _get_config_location(self, name, config=None):
1180
1138
        if config is None:
1181
 
            config = self.get_config_stack()
1182
 
        location = config.get(name)
 
1139
            config = self.get_config()
 
1140
        location = config.get_user_option(name)
1183
1141
        if location == '':
1184
1142
            location = None
1185
1143
        return location
1299
1257
        return result
1300
1258
 
1301
1259
    @needs_read_lock
1302
 
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
1303
 
            repository=None):
 
1260
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
1304
1261
        """Create a new line of development from the branch, into to_bzrdir.
1305
1262
 
1306
1263
        to_bzrdir controls the branch format.
1311
1268
        if (repository_policy is not None and
1312
1269
            repository_policy.requires_stacking()):
1313
1270
            to_bzrdir._format.require_stacking(_skip_repo=True)
1314
 
        result = to_bzrdir.create_branch(repository=repository)
 
1271
        result = to_bzrdir.create_branch()
1315
1272
        result.lock_write()
1316
1273
        try:
1317
1274
            if repository_policy is not None:
1318
1275
                repository_policy.configure_branch(result)
1319
1276
            self.copy_content_into(result, revision_id=revision_id)
1320
 
            master_url = self.get_bound_location()
1321
 
            if master_url is None:
1322
 
                result.set_parent(self.bzrdir.root_transport.base)
1323
 
            else:
1324
 
                result.set_parent(master_url)
 
1277
            result.set_parent(self.bzrdir.root_transport.base)
1325
1278
        finally:
1326
1279
            result.unlock()
1327
1280
        return result
1401
1354
        # TODO: We should probably also check that self.revision_history
1402
1355
        # matches the repository for older branch formats.
1403
1356
        # If looking for the code that cross-checks repository parents against
1404
 
        # the Graph.iter_lefthand_ancestry output, that is now a repository
 
1357
        # the iter_reverse_revision_history output, that is now a repository
1405
1358
        # specific check.
1406
1359
        return result
1407
1360
 
1408
 
    def _get_checkout_format(self, lightweight=False):
 
1361
    def _get_checkout_format(self):
1409
1362
        """Return the most suitable metadir for a checkout of this branch.
1410
1363
        Weaves are used if this branch's repository uses weaves.
1411
1364
        """
1412
 
        format = self.repository.bzrdir.checkout_metadir()
1413
 
        format.set_branch_format(self._format)
 
1365
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
 
1366
            from bzrlib.repofmt import weaverepo
 
1367
            format = bzrdir.BzrDirMetaFormat1()
 
1368
            format.repository_format = weaverepo.RepositoryFormat7()
 
1369
        else:
 
1370
            format = self.repository.bzrdir.checkout_metadir()
 
1371
            format.set_branch_format(self._format)
1414
1372
        return format
1415
1373
 
1416
1374
    def create_clone_on_transport(self, to_transport, revision_id=None,
1417
 
        stacked_on=None, create_prefix=False, use_existing_dir=False,
1418
 
        no_tree=None):
 
1375
        stacked_on=None, create_prefix=False, use_existing_dir=False):
1419
1376
        """Create a clone of this branch and its bzrdir.
1420
1377
 
1421
1378
        :param to_transport: The transport to clone onto.
1434
1391
            revision_id = self.last_revision()
1435
1392
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1436
1393
            revision_id=revision_id, stacked_on=stacked_on,
1437
 
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
1438
 
            no_tree=no_tree)
 
1394
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1439
1395
        return dir_to.open_branch()
1440
1396
 
1441
1397
    def create_checkout(self, to_location, revision_id=None,
1446
1402
        :param to_location: The url to produce the checkout at
1447
1403
        :param revision_id: The revision to check out
1448
1404
        :param lightweight: If True, produce a lightweight checkout, otherwise,
1449
 
            produce a bound branch (heavyweight checkout)
 
1405
        produce a bound branch (heavyweight checkout)
1450
1406
        :param accelerator_tree: A tree which can be used for retrieving file
1451
1407
            contents more quickly than the revision tree, i.e. a workingtree.
1452
1408
            The revision tree will be used for cases where accelerator_tree's
1457
1413
        """
1458
1414
        t = transport.get_transport(to_location)
1459
1415
        t.ensure_base()
1460
 
        format = self._get_checkout_format(lightweight=lightweight)
1461
1416
        if lightweight:
 
1417
            format = self._get_checkout_format()
1462
1418
            checkout = format.initialize_on_transport(t)
1463
1419
            from_branch = BranchReferenceFormat().initialize(checkout, 
1464
1420
                target_branch=self)
1465
1421
        else:
1466
 
            checkout_branch = controldir.ControlDir.create_branch_convenience(
 
1422
            format = self._get_checkout_format()
 
1423
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1467
1424
                to_location, force_new_tree=False, format=format)
1468
1425
            checkout = checkout_branch.bzrdir
1469
1426
            checkout_branch.bind(self)
1497
1454
 
1498
1455
    def reference_parent(self, file_id, path, possible_transports=None):
1499
1456
        """Return the parent branch for a tree-reference file_id
1500
 
 
1501
1457
        :param file_id: The file_id of the tree reference
1502
1458
        :param path: The path of the file_id in the tree
1503
1459
        :return: A branch associated with the file_id
1556
1512
        else:
1557
1513
            raise AssertionError("invalid heads: %r" % (heads,))
1558
1514
 
1559
 
    def heads_to_fetch(self):
1560
 
        """Return the heads that must and that should be fetched to copy this
1561
 
        branch into another repo.
1562
 
 
1563
 
        :returns: a 2-tuple of (must_fetch, if_present_fetch).  must_fetch is a
1564
 
            set of heads that must be fetched.  if_present_fetch is a set of
1565
 
            heads that must be fetched if present, but no error is necessary if
1566
 
            they are not present.
1567
 
        """
1568
 
        # For bzr native formats must_fetch is just the tip, and if_present_fetch
1569
 
        # are the tags.
1570
 
        must_fetch = set([self.last_revision()])
1571
 
        if_present_fetch = set()
1572
 
        c = self.get_config()
1573
 
        include_tags = c.get_user_option_as_bool('branch.fetch_tags',
1574
 
                                                 default=False)
1575
 
        if include_tags:
1576
 
            try:
1577
 
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
1578
 
            except errors.TagsNotSupported:
1579
 
                pass
1580
 
        must_fetch.discard(_mod_revision.NULL_REVISION)
1581
 
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
1582
 
        return must_fetch, if_present_fetch
1583
 
 
1584
 
 
1585
 
class BranchFormat(controldir.ControlComponentFormat):
 
1515
 
 
1516
class BranchFormat(object):
1586
1517
    """An encapsulation of the initialization and open routines for a format.
1587
1518
 
1588
1519
    Formats provide three things:
1600
1531
    object will be created every time regardless.
1601
1532
    """
1602
1533
 
 
1534
    _default_format = None
 
1535
    """The default format used for new branches."""
 
1536
 
 
1537
    _formats = {}
 
1538
    """The known formats."""
 
1539
 
 
1540
    can_set_append_revisions_only = True
 
1541
 
1603
1542
    def __eq__(self, other):
1604
1543
        return self.__class__ is other.__class__
1605
1544
 
1607
1546
        return not (self == other)
1608
1547
 
1609
1548
    @classmethod
1610
 
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1549
    def find_format(klass, a_bzrdir, name=None):
 
1550
        """Return the format for the branch object in a_bzrdir."""
 
1551
        try:
 
1552
            transport = a_bzrdir.get_branch_transport(None, name=name)
 
1553
            format_string = transport.get_bytes("format")
 
1554
            format = klass._formats[format_string]
 
1555
            if isinstance(format, MetaDirBranchFormatFactory):
 
1556
                return format()
 
1557
            return format
 
1558
        except errors.NoSuchFile:
 
1559
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
 
1560
        except KeyError:
 
1561
            raise errors.UnknownFormatError(format=format_string, kind='branch')
 
1562
 
 
1563
    @classmethod
1611
1564
    def get_default_format(klass):
1612
1565
        """Return the current default format."""
1613
 
        return format_registry.get_default()
 
1566
        return klass._default_format
1614
1567
 
1615
1568
    @classmethod
1616
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1617
1569
    def get_formats(klass):
1618
1570
        """Get all the known formats.
1619
1571
 
1620
1572
        Warning: This triggers a load of all lazy registered formats: do not
1621
1573
        use except when that is desireed.
1622
1574
        """
1623
 
        return format_registry._get_all()
 
1575
        result = []
 
1576
        for fmt in klass._formats.values():
 
1577
            if isinstance(fmt, MetaDirBranchFormatFactory):
 
1578
                fmt = fmt()
 
1579
            result.append(fmt)
 
1580
        return result
1624
1581
 
1625
 
    def get_reference(self, controldir, name=None):
1626
 
        """Get the target reference of the branch in controldir.
 
1582
    def get_reference(self, a_bzrdir, name=None):
 
1583
        """Get the target reference of the branch in a_bzrdir.
1627
1584
 
1628
1585
        format probing must have been completed before calling
1629
1586
        this method - it is assumed that the format of the branch
1630
 
        in controldir is correct.
 
1587
        in a_bzrdir is correct.
1631
1588
 
1632
 
        :param controldir: The controldir to get the branch data from.
 
1589
        :param a_bzrdir: The bzrdir to get the branch data from.
1633
1590
        :param name: Name of the colocated branch to fetch
1634
1591
        :return: None if the branch is not a reference branch.
1635
1592
        """
1636
1593
        return None
1637
1594
 
1638
1595
    @classmethod
1639
 
    def set_reference(self, controldir, name, to_branch):
1640
 
        """Set the target reference of the branch in controldir.
 
1596
    def set_reference(self, a_bzrdir, name, to_branch):
 
1597
        """Set the target reference of the branch in a_bzrdir.
1641
1598
 
1642
1599
        format probing must have been completed before calling
1643
1600
        this method - it is assumed that the format of the branch
1644
 
        in controldir is correct.
 
1601
        in a_bzrdir is correct.
1645
1602
 
1646
 
        :param controldir: The controldir to set the branch reference for.
 
1603
        :param a_bzrdir: The bzrdir to set the branch reference for.
1647
1604
        :param name: Name of colocated branch to set, None for default
1648
1605
        :param to_branch: branch that the checkout is to reference
1649
1606
        """
1650
1607
        raise NotImplementedError(self.set_reference)
1651
1608
 
 
1609
    def get_format_string(self):
 
1610
        """Return the ASCII format string that identifies this format."""
 
1611
        raise NotImplementedError(self.get_format_string)
 
1612
 
1652
1613
    def get_format_description(self):
1653
1614
        """Return the short format description for this format."""
1654
1615
        raise NotImplementedError(self.get_format_description)
1655
1616
 
1656
 
    def _run_post_branch_init_hooks(self, controldir, name, branch):
 
1617
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
1657
1618
        hooks = Branch.hooks['post_branch_init']
1658
1619
        if not hooks:
1659
1620
            return
1660
 
        params = BranchInitHookParams(self, controldir, name, branch)
 
1621
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
1661
1622
        for hook in hooks:
1662
1623
            hook(params)
1663
1624
 
1664
 
    def initialize(self, controldir, name=None, repository=None,
1665
 
                   append_revisions_only=None):
1666
 
        """Create a branch of this format in controldir.
1667
 
 
 
1625
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
1626
                           lock_type='metadir', set_format=True):
 
1627
        """Initialize a branch in a bzrdir, with specified files
 
1628
 
 
1629
        :param a_bzrdir: The bzrdir to initialize the branch in
 
1630
        :param utf8_files: The files to create as a list of
 
1631
            (filename, content) tuples
 
1632
        :param name: Name of colocated branch to create, if any
 
1633
        :param set_format: If True, set the format with
 
1634
            self.get_format_string.  (BzrBranch4 has its format set
 
1635
            elsewhere)
 
1636
        :return: a branch in this format
 
1637
        """
 
1638
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
1639
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
 
1640
        lock_map = {
 
1641
            'metadir': ('lock', lockdir.LockDir),
 
1642
            'branch4': ('branch-lock', lockable_files.TransportLock),
 
1643
        }
 
1644
        lock_name, lock_class = lock_map[lock_type]
 
1645
        control_files = lockable_files.LockableFiles(branch_transport,
 
1646
            lock_name, lock_class)
 
1647
        control_files.create_lock()
 
1648
        try:
 
1649
            control_files.lock_write()
 
1650
        except errors.LockContention:
 
1651
            if lock_type != 'branch4':
 
1652
                raise
 
1653
            lock_taken = False
 
1654
        else:
 
1655
            lock_taken = True
 
1656
        if set_format:
 
1657
            utf8_files += [('format', self.get_format_string())]
 
1658
        try:
 
1659
            for (filename, content) in utf8_files:
 
1660
                branch_transport.put_bytes(
 
1661
                    filename, content,
 
1662
                    mode=a_bzrdir._get_file_mode())
 
1663
        finally:
 
1664
            if lock_taken:
 
1665
                control_files.unlock()
 
1666
        branch = self.open(a_bzrdir, name, _found=True)
 
1667
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
1668
        return branch
 
1669
 
 
1670
    def initialize(self, a_bzrdir, name=None):
 
1671
        """Create a branch of this format in a_bzrdir.
 
1672
        
1668
1673
        :param name: Name of the colocated branch to create.
1669
1674
        """
1670
1675
        raise NotImplementedError(self.initialize)
1690
1695
        Note that it is normal for branch to be a RemoteBranch when using tags
1691
1696
        on a RemoteBranch.
1692
1697
        """
1693
 
        return _mod_tag.DisabledTags(branch)
 
1698
        return DisabledTags(branch)
1694
1699
 
1695
1700
    def network_name(self):
1696
1701
        """A simple byte string uniquely identifying this format for RPC calls.
1702
1707
        """
1703
1708
        raise NotImplementedError(self.network_name)
1704
1709
 
1705
 
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
1706
 
            found_repository=None, possible_transports=None):
1707
 
        """Return the branch object for controldir.
 
1710
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
1711
        """Return the branch object for a_bzrdir
1708
1712
 
1709
 
        :param controldir: A ControlDir that contains a branch.
 
1713
        :param a_bzrdir: A BzrDir that contains a branch.
1710
1714
        :param name: Name of colocated branch to open
1711
1715
        :param _found: a private parameter, do not use it. It is used to
1712
1716
            indicate if format probing has already be done.
1716
1720
        raise NotImplementedError(self.open)
1717
1721
 
1718
1722
    @classmethod
1719
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1720
1723
    def register_format(klass, format):
1721
1724
        """Register a metadir format.
1722
 
 
 
1725
        
1723
1726
        See MetaDirBranchFormatFactory for the ability to register a format
1724
1727
        without loading the code the format needs until it is actually used.
1725
1728
        """
1726
 
        format_registry.register(format)
 
1729
        klass._formats[format.get_format_string()] = format
 
1730
        # Metadir formats have a network name of their format string, and get
 
1731
        # registered as factories.
 
1732
        if isinstance(format, MetaDirBranchFormatFactory):
 
1733
            network_format_registry.register(format.get_format_string(), format)
 
1734
        else:
 
1735
            network_format_registry.register(format.get_format_string(),
 
1736
                format.__class__)
1727
1737
 
1728
1738
    @classmethod
1729
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1730
1739
    def set_default_format(klass, format):
1731
 
        format_registry.set_default(format)
 
1740
        klass._default_format = format
1732
1741
 
1733
1742
    def supports_set_append_revisions_only(self):
1734
1743
        """True if this format supports set_append_revisions_only."""
1738
1747
        """True if this format records a stacked-on branch."""
1739
1748
        return False
1740
1749
 
1741
 
    def supports_leaving_lock(self):
1742
 
        """True if this format supports leaving locks in place."""
1743
 
        return False # by default
1744
 
 
1745
1750
    @classmethod
1746
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1747
1751
    def unregister_format(klass, format):
1748
 
        format_registry.remove(format)
 
1752
        del klass._formats[format.get_format_string()]
1749
1753
 
1750
1754
    def __str__(self):
1751
1755
        return self.get_format_description().rstrip()
1754
1758
        """True if this format supports tags stored in the branch"""
1755
1759
        return False  # by default
1756
1760
 
1757
 
    def tags_are_versioned(self):
1758
 
        """Whether the tag container for this branch versions tags."""
1759
 
        return False
1760
 
 
1761
 
    def supports_tags_referencing_ghosts(self):
1762
 
        """True if tags can reference ghost revisions."""
1763
 
        return True
1764
 
 
1765
1761
 
1766
1762
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1767
1763
    """A factory for a BranchFormat object, permitting simple lazy registration.
1781
1777
        """
1782
1778
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
1783
1779
        self._format_string = format_string
1784
 
 
 
1780
        
1785
1781
    def get_format_string(self):
1786
1782
        """See BranchFormat.get_format_string."""
1787
1783
        return self._format_string
1804
1800
        These are all empty initially, because by default nothing should get
1805
1801
        notified.
1806
1802
        """
1807
 
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1808
 
        self.add_hook('set_rh',
 
1803
        Hooks.__init__(self)
 
1804
        self.create_hook(HookPoint('set_rh',
1809
1805
            "Invoked whenever the revision history has been set via "
1810
1806
            "set_revision_history. The api signature is (branch, "
1811
1807
            "revision_history), and the branch will be write-locked. "
1812
1808
            "The set_rh hook can be expensive for bzr to trigger, a better "
1813
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
1814
 
        self.add_hook('open',
 
1809
            "hook to use is Branch.post_change_branch_tip.", (0, 15), None))
 
1810
        self.create_hook(HookPoint('open',
1815
1811
            "Called with the Branch object that has been opened after a "
1816
 
            "branch is opened.", (1, 8))
1817
 
        self.add_hook('post_push',
 
1812
            "branch is opened.", (1, 8), None))
 
1813
        self.create_hook(HookPoint('post_push',
1818
1814
            "Called after a push operation completes. post_push is called "
1819
1815
            "with a bzrlib.branch.BranchPushResult object and only runs in the "
1820
 
            "bzr client.", (0, 15))
1821
 
        self.add_hook('post_pull',
 
1816
            "bzr client.", (0, 15), None))
 
1817
        self.create_hook(HookPoint('post_pull',
1822
1818
            "Called after a pull operation completes. post_pull is called "
1823
1819
            "with a bzrlib.branch.PullResult object and only runs in the "
1824
 
            "bzr client.", (0, 15))
1825
 
        self.add_hook('pre_commit',
 
1820
            "bzr client.", (0, 15), None))
 
1821
        self.create_hook(HookPoint('pre_commit',
1826
1822
            "Called after a commit is calculated but before it is "
1827
1823
            "completed. pre_commit is called with (local, master, old_revno, "
1828
1824
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1831
1827
            "basis revision. hooks MUST NOT modify this delta. "
1832
1828
            " future_tree is an in-memory tree obtained from "
1833
1829
            "CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1834
 
            "tree.", (0,91))
1835
 
        self.add_hook('post_commit',
 
1830
            "tree.", (0,91), None))
 
1831
        self.create_hook(HookPoint('post_commit',
1836
1832
            "Called in the bzr client after a commit has completed. "
1837
1833
            "post_commit is called with (local, master, old_revno, old_revid, "
1838
1834
            "new_revno, new_revid). old_revid is NULL_REVISION for the first "
1839
 
            "commit to a branch.", (0, 15))
1840
 
        self.add_hook('post_uncommit',
 
1835
            "commit to a branch.", (0, 15), None))
 
1836
        self.create_hook(HookPoint('post_uncommit',
1841
1837
            "Called in the bzr client after an uncommit completes. "
1842
1838
            "post_uncommit is called with (local, master, old_revno, "
1843
1839
            "old_revid, new_revno, new_revid) where local is the local branch "
1844
1840
            "or None, master is the target branch, and an empty branch "
1845
 
            "receives new_revno of 0, new_revid of None.", (0, 15))
1846
 
        self.add_hook('pre_change_branch_tip',
 
1841
            "receives new_revno of 0, new_revid of None.", (0, 15), None))
 
1842
        self.create_hook(HookPoint('pre_change_branch_tip',
1847
1843
            "Called in bzr client and server before a change to the tip of a "
1848
1844
            "branch is made. pre_change_branch_tip is called with a "
1849
1845
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1850
 
            "commit, uncommit will all trigger this hook.", (1, 6))
1851
 
        self.add_hook('post_change_branch_tip',
 
1846
            "commit, uncommit will all trigger this hook.", (1, 6), None))
 
1847
        self.create_hook(HookPoint('post_change_branch_tip',
1852
1848
            "Called in bzr client and server after a change to the tip of a "
1853
1849
            "branch is made. post_change_branch_tip is called with a "
1854
1850
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1855
 
            "commit, uncommit will all trigger this hook.", (1, 4))
1856
 
        self.add_hook('transform_fallback_location',
 
1851
            "commit, uncommit will all trigger this hook.", (1, 4), None))
 
1852
        self.create_hook(HookPoint('transform_fallback_location',
1857
1853
            "Called when a stacked branch is activating its fallback "
1858
1854
            "locations. transform_fallback_location is called with (branch, "
1859
1855
            "url), and should return a new url. Returning the same url "
1864
1860
            "fallback locations have not been activated. When there are "
1865
1861
            "multiple hooks installed for transform_fallback_location, "
1866
1862
            "all are called with the url returned from the previous hook."
1867
 
            "The order is however undefined.", (1, 9))
1868
 
        self.add_hook('automatic_tag_name',
 
1863
            "The order is however undefined.", (1, 9), None))
 
1864
        self.create_hook(HookPoint('automatic_tag_name',
1869
1865
            "Called to determine an automatic tag name for a revision. "
1870
1866
            "automatic_tag_name is called with (branch, revision_id) and "
1871
1867
            "should return a tag name or None if no tag name could be "
1872
1868
            "determined. The first non-None tag name returned will be used.",
1873
 
            (2, 2))
1874
 
        self.add_hook('post_branch_init',
 
1869
            (2, 2), None))
 
1870
        self.create_hook(HookPoint('post_branch_init',
1875
1871
            "Called after new branch initialization completes. "
1876
1872
            "post_branch_init is called with a "
1877
1873
            "bzrlib.branch.BranchInitHookParams. "
1878
1874
            "Note that init, branch and checkout (both heavyweight and "
1879
 
            "lightweight) will all trigger this hook.", (2, 2))
1880
 
        self.add_hook('post_switch',
 
1875
            "lightweight) will all trigger this hook.", (2, 2), None))
 
1876
        self.create_hook(HookPoint('post_switch',
1881
1877
            "Called after a checkout switches branch. "
1882
1878
            "post_switch is called with a "
1883
 
            "bzrlib.branch.SwitchHookParams.", (2, 2))
 
1879
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
1884
1880
 
1885
1881
 
1886
1882
 
1889
1885
 
1890
1886
 
1891
1887
class ChangeBranchTipParams(object):
1892
 
    """Object holding parameters passed to `*_change_branch_tip` hooks.
 
1888
    """Object holding parameters passed to *_change_branch_tip hooks.
1893
1889
 
1894
1890
    There are 5 fields that hooks may wish to access:
1895
1891
 
1927
1923
 
1928
1924
 
1929
1925
class BranchInitHookParams(object):
1930
 
    """Object holding parameters passed to `*_branch_init` hooks.
 
1926
    """Object holding parameters passed to *_branch_init hooks.
1931
1927
 
1932
1928
    There are 4 fields that hooks may wish to access:
1933
1929
 
1934
1930
    :ivar format: the branch format
1935
 
    :ivar bzrdir: the ControlDir where the branch will be/has been initialized
 
1931
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
1936
1932
    :ivar name: name of colocated branch, if any (or None)
1937
1933
    :ivar branch: the branch created
1938
1934
 
1941
1937
    branch, which refer to the original branch.
1942
1938
    """
1943
1939
 
1944
 
    def __init__(self, format, controldir, name, branch):
 
1940
    def __init__(self, format, a_bzrdir, name, branch):
1945
1941
        """Create a group of BranchInitHook parameters.
1946
1942
 
1947
1943
        :param format: the branch format
1948
 
        :param controldir: the ControlDir where the branch will be/has been
 
1944
        :param a_bzrdir: the BzrDir where the branch will be/has been
1949
1945
            initialized
1950
1946
        :param name: name of colocated branch, if any (or None)
1951
1947
        :param branch: the branch created
1955
1951
        in branch, which refer to the original branch.
1956
1952
        """
1957
1953
        self.format = format
1958
 
        self.bzrdir = controldir
 
1954
        self.bzrdir = a_bzrdir
1959
1955
        self.name = name
1960
1956
        self.branch = branch
1961
1957
 
1967
1963
 
1968
1964
 
1969
1965
class SwitchHookParams(object):
1970
 
    """Object holding parameters passed to `*_switch` hooks.
 
1966
    """Object holding parameters passed to *_switch hooks.
1971
1967
 
1972
1968
    There are 4 fields that hooks may wish to access:
1973
1969
 
1974
 
    :ivar control_dir: ControlDir of the checkout to change
 
1970
    :ivar control_dir: BzrDir of the checkout to change
1975
1971
    :ivar to_branch: branch that the checkout is to reference
1976
1972
    :ivar force: skip the check for local commits in a heavy checkout
1977
1973
    :ivar revision_id: revision ID to switch to (or None)
1980
1976
    def __init__(self, control_dir, to_branch, force, revision_id):
1981
1977
        """Create a group of SwitchHook parameters.
1982
1978
 
1983
 
        :param control_dir: ControlDir of the checkout to change
 
1979
        :param control_dir: BzrDir of the checkout to change
1984
1980
        :param to_branch: branch that the checkout is to reference
1985
1981
        :param force: skip the check for local commits in a heavy checkout
1986
1982
        :param revision_id: revision ID to switch to (or None)
1999
1995
            self.revision_id)
2000
1996
 
2001
1997
 
2002
 
class BranchFormatMetadir(bzrdir.BzrDirMetaComponentFormat, BranchFormat):
2003
 
    """Base class for branch formats that live in meta directories.
 
1998
class BzrBranchFormat4(BranchFormat):
 
1999
    """Bzr branch format 4.
 
2000
 
 
2001
    This format has:
 
2002
     - a revision-history file.
 
2003
     - a branch-lock lock file [ to be shared with the bzrdir ]
2004
2004
    """
2005
2005
 
 
2006
    def get_format_description(self):
 
2007
        """See BranchFormat.get_format_description()."""
 
2008
        return "Branch format 4"
 
2009
 
 
2010
    def initialize(self, a_bzrdir, name=None):
 
2011
        """Create a branch of this format in a_bzrdir."""
 
2012
        utf8_files = [('revision-history', ''),
 
2013
                      ('branch-name', ''),
 
2014
                      ]
 
2015
        return self._initialize_helper(a_bzrdir, utf8_files, name=name,
 
2016
                                       lock_type='branch4', set_format=False)
 
2017
 
2006
2018
    def __init__(self):
2007
 
        BranchFormat.__init__(self)
2008
 
        bzrdir.BzrDirMetaComponentFormat.__init__(self)
2009
 
 
2010
 
    @classmethod
2011
 
    def find_format(klass, controldir, name=None):
2012
 
        """Return the format for the branch object in controldir."""
2013
 
        try:
2014
 
            transport = controldir.get_branch_transport(None, name=name)
2015
 
        except errors.NoSuchFile:
2016
 
            raise errors.NotBranchError(path=name, bzrdir=controldir)
2017
 
        try:
2018
 
            format_string = transport.get_bytes("format")
2019
 
        except errors.NoSuchFile:
2020
 
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
2021
 
        return klass._find_format(format_registry, 'branch', format_string)
 
2019
        super(BzrBranchFormat4, self).__init__()
 
2020
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
 
2021
 
 
2022
    def network_name(self):
 
2023
        """The network name for this format is the control dirs disk label."""
 
2024
        return self._matchingbzrdir.get_format_string()
 
2025
 
 
2026
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
2027
        """See BranchFormat.open()."""
 
2028
        if not _found:
 
2029
            # we are being called directly and must probe.
 
2030
            raise NotImplementedError
 
2031
        return BzrBranch(_format=self,
 
2032
                         _control_files=a_bzrdir._control_files,
 
2033
                         a_bzrdir=a_bzrdir,
 
2034
                         name=name,
 
2035
                         _repository=a_bzrdir.open_repository())
 
2036
 
 
2037
    def __str__(self):
 
2038
        return "Bazaar-NG branch format 4"
 
2039
 
 
2040
 
 
2041
class BranchFormatMetadir(BranchFormat):
 
2042
    """Common logic for meta-dir based branch formats."""
2022
2043
 
2023
2044
    def _branch_class(self):
2024
2045
        """What class to instantiate on open calls."""
2025
2046
        raise NotImplementedError(self._branch_class)
2026
2047
 
2027
 
    def _get_initial_config(self, append_revisions_only=None):
2028
 
        if append_revisions_only:
2029
 
            return "append_revisions_only = True\n"
2030
 
        else:
2031
 
            # Avoid writing anything if append_revisions_only is disabled,
2032
 
            # as that is the default.
2033
 
            return ""
2034
 
 
2035
 
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
2036
 
                           repository=None):
2037
 
        """Initialize a branch in a bzrdir, with specified files
2038
 
 
2039
 
        :param a_bzrdir: The bzrdir to initialize the branch in
2040
 
        :param utf8_files: The files to create as a list of
2041
 
            (filename, content) tuples
2042
 
        :param name: Name of colocated branch to create, if any
2043
 
        :return: a branch in this format
 
2048
    def network_name(self):
 
2049
        """A simple byte string uniquely identifying this format for RPC calls.
 
2050
 
 
2051
        Metadir branch formats use their format string.
2044
2052
        """
2045
 
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
2046
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2047
 
        control_files = lockable_files.LockableFiles(branch_transport,
2048
 
            'lock', lockdir.LockDir)
2049
 
        control_files.create_lock()
2050
 
        control_files.lock_write()
2051
 
        try:
2052
 
            utf8_files += [('format', self.get_format_string())]
2053
 
            for (filename, content) in utf8_files:
2054
 
                branch_transport.put_bytes(
2055
 
                    filename, content,
2056
 
                    mode=a_bzrdir._get_file_mode())
2057
 
        finally:
2058
 
            control_files.unlock()
2059
 
        branch = self.open(a_bzrdir, name, _found=True,
2060
 
                found_repository=repository)
2061
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2062
 
        return branch
 
2053
        return self.get_format_string()
2063
2054
 
2064
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2065
 
            found_repository=None, possible_transports=None):
 
2055
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2066
2056
        """See BranchFormat.open()."""
2067
2057
        if not _found:
2068
 
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
 
2058
            format = BranchFormat.find_format(a_bzrdir, name=name)
2069
2059
            if format.__class__ != self.__class__:
2070
2060
                raise AssertionError("wrong format %r found for %r" %
2071
2061
                    (format, self))
2073
2063
        try:
2074
2064
            control_files = lockable_files.LockableFiles(transport, 'lock',
2075
2065
                                                         lockdir.LockDir)
2076
 
            if found_repository is None:
2077
 
                found_repository = a_bzrdir.find_repository()
2078
2066
            return self._branch_class()(_format=self,
2079
2067
                              _control_files=control_files,
2080
2068
                              name=name,
2081
2069
                              a_bzrdir=a_bzrdir,
2082
 
                              _repository=found_repository,
2083
 
                              ignore_fallbacks=ignore_fallbacks,
2084
 
                              possible_transports=possible_transports)
 
2070
                              _repository=a_bzrdir.find_repository(),
 
2071
                              ignore_fallbacks=ignore_fallbacks)
2085
2072
        except errors.NoSuchFile:
2086
2073
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2087
2074
 
2088
 
    @property
2089
 
    def _matchingbzrdir(self):
2090
 
        ret = bzrdir.BzrDirMetaFormat1()
2091
 
        ret.set_branch_format(self)
2092
 
        return ret
 
2075
    def __init__(self):
 
2076
        super(BranchFormatMetadir, self).__init__()
 
2077
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
2078
        self._matchingbzrdir.set_branch_format(self)
2093
2079
 
2094
2080
    def supports_tags(self):
2095
2081
        return True
2096
2082
 
2097
 
    def supports_leaving_lock(self):
2098
 
        return True
2099
 
 
2100
2083
 
2101
2084
class BzrBranchFormat5(BranchFormatMetadir):
2102
2085
    """Bzr branch format 5.
2114
2097
    def _branch_class(self):
2115
2098
        return BzrBranch5
2116
2099
 
2117
 
    @classmethod
2118
 
    def get_format_string(cls):
 
2100
    def get_format_string(self):
2119
2101
        """See BranchFormat.get_format_string()."""
2120
2102
        return "Bazaar-NG branch format 5\n"
2121
2103
 
2123
2105
        """See BranchFormat.get_format_description()."""
2124
2106
        return "Branch format 5"
2125
2107
 
2126
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2127
 
                   append_revisions_only=None):
 
2108
    def initialize(self, a_bzrdir, name=None):
2128
2109
        """Create a branch of this format in a_bzrdir."""
2129
 
        if append_revisions_only:
2130
 
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2131
2110
        utf8_files = [('revision-history', ''),
2132
2111
                      ('branch-name', ''),
2133
2112
                      ]
2134
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2113
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2135
2114
 
2136
2115
    def supports_tags(self):
2137
2116
        return False
2151
2130
    def _branch_class(self):
2152
2131
        return BzrBranch6
2153
2132
 
2154
 
    @classmethod
2155
 
    def get_format_string(cls):
 
2133
    def get_format_string(self):
2156
2134
        """See BranchFormat.get_format_string()."""
2157
2135
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
2158
2136
 
2160
2138
        """See BranchFormat.get_format_description()."""
2161
2139
        return "Branch format 6"
2162
2140
 
2163
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2164
 
                   append_revisions_only=None):
 
2141
    def initialize(self, a_bzrdir, name=None):
2165
2142
        """Create a branch of this format in a_bzrdir."""
2166
2143
        utf8_files = [('last-revision', '0 null:\n'),
2167
 
                      ('branch.conf',
2168
 
                          self._get_initial_config(append_revisions_only)),
 
2144
                      ('branch.conf', ''),
2169
2145
                      ('tags', ''),
2170
2146
                      ]
2171
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2147
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2172
2148
 
2173
2149
    def make_tags(self, branch):
2174
2150
        """See bzrlib.branch.BranchFormat.make_tags()."""
2175
 
        return _mod_tag.BasicTags(branch)
 
2151
        return BasicTags(branch)
2176
2152
 
2177
2153
    def supports_set_append_revisions_only(self):
2178
2154
        return True
2184
2160
    def _branch_class(self):
2185
2161
        return BzrBranch8
2186
2162
 
2187
 
    @classmethod
2188
 
    def get_format_string(cls):
 
2163
    def get_format_string(self):
2189
2164
        """See BranchFormat.get_format_string()."""
2190
2165
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2191
2166
 
2193
2168
        """See BranchFormat.get_format_description()."""
2194
2169
        return "Branch format 8"
2195
2170
 
2196
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2197
 
                   append_revisions_only=None):
 
2171
    def initialize(self, a_bzrdir, name=None):
2198
2172
        """Create a branch of this format in a_bzrdir."""
2199
2173
        utf8_files = [('last-revision', '0 null:\n'),
2200
 
                      ('branch.conf',
2201
 
                          self._get_initial_config(append_revisions_only)),
 
2174
                      ('branch.conf', ''),
2202
2175
                      ('tags', ''),
2203
2176
                      ('references', '')
2204
2177
                      ]
2205
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2178
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2179
 
 
2180
    def __init__(self):
 
2181
        super(BzrBranchFormat8, self).__init__()
 
2182
        self._matchingbzrdir.repository_format = \
 
2183
            RepositoryFormatKnitPack5RichRoot()
2206
2184
 
2207
2185
    def make_tags(self, branch):
2208
2186
        """See bzrlib.branch.BranchFormat.make_tags()."""
2209
 
        return _mod_tag.BasicTags(branch)
 
2187
        return BasicTags(branch)
2210
2188
 
2211
2189
    def supports_set_append_revisions_only(self):
2212
2190
        return True
2217
2195
    supports_reference_locations = True
2218
2196
 
2219
2197
 
2220
 
class BzrBranchFormat7(BranchFormatMetadir):
 
2198
class BzrBranchFormat7(BzrBranchFormat8):
2221
2199
    """Branch format with last-revision, tags, and a stacked location pointer.
2222
2200
 
2223
2201
    The stacked location pointer is passed down to the repository and requires
2226
2204
    This format was introduced in bzr 1.6.
2227
2205
    """
2228
2206
 
2229
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2230
 
                   append_revisions_only=None):
 
2207
    def initialize(self, a_bzrdir, name=None):
2231
2208
        """Create a branch of this format in a_bzrdir."""
2232
2209
        utf8_files = [('last-revision', '0 null:\n'),
2233
 
                      ('branch.conf',
2234
 
                          self._get_initial_config(append_revisions_only)),
 
2210
                      ('branch.conf', ''),
2235
2211
                      ('tags', ''),
2236
2212
                      ]
2237
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2213
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2238
2214
 
2239
2215
    def _branch_class(self):
2240
2216
        return BzrBranch7
2241
2217
 
2242
 
    @classmethod
2243
 
    def get_format_string(cls):
 
2218
    def get_format_string(self):
2244
2219
        """See BranchFormat.get_format_string()."""
2245
2220
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2246
2221
 
2251
2226
    def supports_set_append_revisions_only(self):
2252
2227
        return True
2253
2228
 
2254
 
    def supports_stacking(self):
2255
 
        return True
2256
 
 
2257
 
    def make_tags(self, branch):
2258
 
        """See bzrlib.branch.BranchFormat.make_tags()."""
2259
 
        return _mod_tag.BasicTags(branch)
2260
 
 
2261
2229
    supports_reference_locations = False
2262
2230
 
2263
2231
 
2264
 
class BranchReferenceFormat(BranchFormatMetadir):
 
2232
class BranchReferenceFormat(BranchFormat):
2265
2233
    """Bzr branch reference format.
2266
2234
 
2267
2235
    Branch references are used in implementing checkouts, they
2272
2240
     - a format string
2273
2241
    """
2274
2242
 
2275
 
    @classmethod
2276
 
    def get_format_string(cls):
 
2243
    def get_format_string(self):
2277
2244
        """See BranchFormat.get_format_string()."""
2278
2245
        return "Bazaar-NG Branch Reference Format 1\n"
2279
2246
 
2291
2258
        transport = a_bzrdir.get_branch_transport(None, name=name)
2292
2259
        location = transport.put_bytes('location', to_branch.base)
2293
2260
 
2294
 
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2295
 
            repository=None, append_revisions_only=None):
 
2261
    def initialize(self, a_bzrdir, name=None, target_branch=None):
2296
2262
        """Create a branch of this format in a_bzrdir."""
2297
2263
        if target_branch is None:
2298
2264
            # this format does not implement branch itself, thus the implicit
2299
2265
            # creation contract must see it as uninitializable
2300
2266
            raise errors.UninitializableFormat(self)
2301
2267
        mutter('creating branch reference in %s', a_bzrdir.user_url)
2302
 
        if a_bzrdir._format.fixed_components:
2303
 
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
2304
2268
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2305
2269
        branch_transport.put_bytes('location',
2306
 
            target_branch.user_url)
 
2270
            target_branch.bzrdir.user_url)
2307
2271
        branch_transport.put_bytes('format', self.get_format_string())
2308
2272
        branch = self.open(
2309
2273
            a_bzrdir, name, _found=True,
2311
2275
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2312
2276
        return branch
2313
2277
 
 
2278
    def __init__(self):
 
2279
        super(BranchReferenceFormat, self).__init__()
 
2280
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
2281
        self._matchingbzrdir.set_branch_format(self)
 
2282
 
2314
2283
    def _make_reference_clone_function(format, a_branch):
2315
2284
        """Create a clone() routine for a branch dynamically."""
2316
2285
        def clone(to_bzrdir, revision_id=None,
2323
2292
        return clone
2324
2293
 
2325
2294
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2326
 
             possible_transports=None, ignore_fallbacks=False,
2327
 
             found_repository=None):
 
2295
             possible_transports=None, ignore_fallbacks=False):
2328
2296
        """Return the branch that the branch reference in a_bzrdir points at.
2329
2297
 
2330
2298
        :param a_bzrdir: A BzrDir that contains a branch.
2339
2307
        :param possible_transports: An optional reusable transports list.
2340
2308
        """
2341
2309
        if not _found:
2342
 
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
 
2310
            format = BranchFormat.find_format(a_bzrdir, name=name)
2343
2311
            if format.__class__ != self.__class__:
2344
2312
                raise AssertionError("wrong format %r found for %r" %
2345
2313
                    (format, self))
2346
2314
        if location is None:
2347
2315
            location = self.get_reference(a_bzrdir, name)
2348
 
        real_bzrdir = controldir.ControlDir.open(
 
2316
        real_bzrdir = bzrdir.BzrDir.open(
2349
2317
            location, possible_transports=possible_transports)
2350
2318
        result = real_bzrdir.open_branch(name=name, 
2351
 
            ignore_fallbacks=ignore_fallbacks,
2352
 
            possible_transports=possible_transports)
 
2319
            ignore_fallbacks=ignore_fallbacks)
2353
2320
        # this changes the behaviour of result.clone to create a new reference
2354
2321
        # rather than a copy of the content of the branch.
2355
2322
        # I did not use a proxy object because that needs much more extensive
2362
2329
        return result
2363
2330
 
2364
2331
 
2365
 
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
2366
 
    """Branch format registry."""
2367
 
 
2368
 
    def __init__(self, other_registry=None):
2369
 
        super(BranchFormatRegistry, self).__init__(other_registry)
2370
 
        self._default_format = None
2371
 
 
2372
 
    def set_default(self, format):
2373
 
        self._default_format = format
2374
 
 
2375
 
    def get_default(self):
2376
 
        return self._default_format
2377
 
 
2378
 
 
2379
2332
network_format_registry = registry.FormatRegistry()
2380
2333
"""Registry of formats indexed by their network name.
2381
2334
 
2384
2337
BranchFormat.network_name() for more detail.
2385
2338
"""
2386
2339
 
2387
 
format_registry = BranchFormatRegistry(network_format_registry)
2388
 
 
2389
2340
 
2390
2341
# formats which have no format string are not discoverable
2391
2342
# and not independently creatable, so are not registered.
2393
2344
__format6 = BzrBranchFormat6()
2394
2345
__format7 = BzrBranchFormat7()
2395
2346
__format8 = BzrBranchFormat8()
2396
 
format_registry.register(__format5)
2397
 
format_registry.register(BranchReferenceFormat())
2398
 
format_registry.register(__format6)
2399
 
format_registry.register(__format7)
2400
 
format_registry.register(__format8)
2401
 
format_registry.set_default(__format7)
 
2347
BranchFormat.register_format(__format5)
 
2348
BranchFormat.register_format(BranchReferenceFormat())
 
2349
BranchFormat.register_format(__format6)
 
2350
BranchFormat.register_format(__format7)
 
2351
BranchFormat.register_format(__format8)
 
2352
BranchFormat.set_default_format(__format7)
 
2353
_legacy_formats = [BzrBranchFormat4(),
 
2354
    ]
 
2355
network_format_registry.register(
 
2356
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2402
2357
 
2403
2358
 
2404
2359
class BranchWriteLockResult(LogicalLockResult):
2436
2391
 
2437
2392
    def __init__(self, _format=None,
2438
2393
                 _control_files=None, a_bzrdir=None, name=None,
2439
 
                 _repository=None, ignore_fallbacks=False,
2440
 
                 possible_transports=None):
 
2394
                 _repository=None, ignore_fallbacks=False):
2441
2395
        """Create new branch object at a particular location."""
2442
2396
        if a_bzrdir is None:
2443
2397
            raise ValueError('a_bzrdir must be supplied')
2444
2398
        else:
2445
2399
            self.bzrdir = a_bzrdir
2446
 
        self._user_transport = self.bzrdir.transport.clone('..')
2447
 
        if name is not None:
2448
 
            self._user_transport.set_segment_parameter(
2449
 
                "branch", urlutils.escape(name))
2450
 
        self._base = self._user_transport.base
 
2400
        self._base = self.bzrdir.transport.clone('..').base
2451
2401
        self.name = name
 
2402
        # XXX: We should be able to just do
 
2403
        #   self.base = self.bzrdir.root_transport.base
 
2404
        # but this does not quite work yet -- mbp 20080522
2452
2405
        self._format = _format
2453
2406
        if _control_files is None:
2454
2407
            raise ValueError('BzrBranch _control_files is None')
2455
2408
        self.control_files = _control_files
2456
2409
        self._transport = _control_files._transport
2457
2410
        self.repository = _repository
2458
 
        Branch.__init__(self, possible_transports)
 
2411
        Branch.__init__(self)
2459
2412
 
2460
2413
    def __str__(self):
2461
 
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
 
2414
        if self.name is None:
 
2415
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
 
2416
        else:
 
2417
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
 
2418
                self.name)
2462
2419
 
2463
2420
    __repr__ = __str__
2464
2421
 
2468
2425
 
2469
2426
    base = property(_get_base, doc="The URL for the root of this branch.")
2470
2427
 
2471
 
    @property
2472
 
    def user_transport(self):
2473
 
        return self._user_transport
2474
 
 
2475
2428
    def _get_config(self):
2476
 
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
2477
 
 
2478
 
    def _get_config_store(self):
2479
 
        return _mod_config.BranchStore(self)
 
2429
        return TransportConfig(self._transport, 'branch.conf')
2480
2430
 
2481
2431
    def is_locked(self):
2482
2432
        return self.control_files.is_locked()
2557
2507
        """See Branch.print_file."""
2558
2508
        return self.repository.print_file(file, revision_id)
2559
2509
 
 
2510
    def _write_revision_history(self, history):
 
2511
        """Factored out of set_revision_history.
 
2512
 
 
2513
        This performs the actual writing to disk.
 
2514
        It is intended to be called by BzrBranch5.set_revision_history."""
 
2515
        self._transport.put_bytes(
 
2516
            'revision-history', '\n'.join(history),
 
2517
            mode=self.bzrdir._get_file_mode())
 
2518
 
 
2519
    @needs_write_lock
 
2520
    def set_revision_history(self, rev_history):
 
2521
        """See Branch.set_revision_history."""
 
2522
        if 'evil' in debug.debug_flags:
 
2523
            mutter_callsite(3, "set_revision_history scales with history.")
 
2524
        check_not_reserved_id = _mod_revision.check_not_reserved_id
 
2525
        for rev_id in rev_history:
 
2526
            check_not_reserved_id(rev_id)
 
2527
        if Branch.hooks['post_change_branch_tip']:
 
2528
            # Don't calculate the last_revision_info() if there are no hooks
 
2529
            # that will use it.
 
2530
            old_revno, old_revid = self.last_revision_info()
 
2531
        if len(rev_history) == 0:
 
2532
            revid = _mod_revision.NULL_REVISION
 
2533
        else:
 
2534
            revid = rev_history[-1]
 
2535
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
 
2536
        self._write_revision_history(rev_history)
 
2537
        self._clear_cached_state()
 
2538
        self._cache_revision_history(rev_history)
 
2539
        for hook in Branch.hooks['set_rh']:
 
2540
            hook(self, rev_history)
 
2541
        if Branch.hooks['post_change_branch_tip']:
 
2542
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2543
 
 
2544
    def _synchronize_history(self, destination, revision_id):
 
2545
        """Synchronize last revision and revision history between branches.
 
2546
 
 
2547
        This version is most efficient when the destination is also a
 
2548
        BzrBranch5, but works for BzrBranch6 as long as the revision
 
2549
        history is the true lefthand parent history, and all of the revisions
 
2550
        are in the destination's repository.  If not, set_revision_history
 
2551
        will fail.
 
2552
 
 
2553
        :param destination: The branch to copy the history into
 
2554
        :param revision_id: The revision-id to truncate history at.  May
 
2555
          be None to copy complete history.
 
2556
        """
 
2557
        if not isinstance(destination._format, BzrBranchFormat5):
 
2558
            super(BzrBranch, self)._synchronize_history(
 
2559
                destination, revision_id)
 
2560
            return
 
2561
        if revision_id == _mod_revision.NULL_REVISION:
 
2562
            new_history = []
 
2563
        else:
 
2564
            new_history = self.revision_history()
 
2565
        if revision_id is not None and new_history != []:
 
2566
            try:
 
2567
                new_history = new_history[:new_history.index(revision_id) + 1]
 
2568
            except ValueError:
 
2569
                rev = self.repository.get_revision(revision_id)
 
2570
                new_history = rev.get_history(self.repository)[1:]
 
2571
        destination.set_revision_history(new_history)
 
2572
 
2560
2573
    @needs_write_lock
2561
2574
    def set_last_revision_info(self, revno, revision_id):
2562
 
        if not revision_id or not isinstance(revision_id, basestring):
2563
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2575
        """Set the last revision of this branch.
 
2576
 
 
2577
        The caller is responsible for checking that the revno is correct
 
2578
        for this revision id.
 
2579
 
 
2580
        It may be possible to set the branch last revision to an id not
 
2581
        present in the repository.  However, branches can also be
 
2582
        configured to check constraints on history, in which case this may not
 
2583
        be permitted.
 
2584
        """
2564
2585
        revision_id = _mod_revision.ensure_null(revision_id)
2565
 
        old_revno, old_revid = self.last_revision_info()
2566
 
        if self.get_append_revisions_only():
2567
 
            self._check_history_violation(revision_id)
2568
 
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2569
 
        self._write_last_revision_info(revno, revision_id)
2570
 
        self._clear_cached_state()
2571
 
        self._last_revision_info_cache = revno, revision_id
2572
 
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2586
        # this old format stores the full history, but this api doesn't
 
2587
        # provide it, so we must generate, and might as well check it's
 
2588
        # correct
 
2589
        history = self._lefthand_history(revision_id)
 
2590
        if len(history) != revno:
 
2591
            raise AssertionError('%d != %d' % (len(history), revno))
 
2592
        self.set_revision_history(history)
 
2593
 
 
2594
    def _gen_revision_history(self):
 
2595
        history = self._transport.get_bytes('revision-history').split('\n')
 
2596
        if history[-1:] == ['']:
 
2597
            # There shouldn't be a trailing newline, but just in case.
 
2598
            history.pop()
 
2599
        return history
 
2600
 
 
2601
    @needs_write_lock
 
2602
    def generate_revision_history(self, revision_id, last_rev=None,
 
2603
        other_branch=None):
 
2604
        """Create a new revision history that will finish with revision_id.
 
2605
 
 
2606
        :param revision_id: the new tip to use.
 
2607
        :param last_rev: The previous last_revision. If not None, then this
 
2608
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
2609
        :param other_branch: The other branch that DivergedBranches should
 
2610
            raise with respect to.
 
2611
        """
 
2612
        self.set_revision_history(self._lefthand_history(revision_id,
 
2613
            last_rev, other_branch))
2573
2614
 
2574
2615
    def basis_tree(self):
2575
2616
        """See Branch.basis_tree."""
2584
2625
                pass
2585
2626
        return None
2586
2627
 
 
2628
    def _basic_push(self, target, overwrite, stop_revision):
 
2629
        """Basic implementation of push without bound branches or hooks.
 
2630
 
 
2631
        Must be called with source read locked and target write locked.
 
2632
        """
 
2633
        result = BranchPushResult()
 
2634
        result.source_branch = self
 
2635
        result.target_branch = target
 
2636
        result.old_revno, result.old_revid = target.last_revision_info()
 
2637
        self.update_references(target)
 
2638
        if result.old_revid != self.last_revision():
 
2639
            # We assume that during 'push' this repository is closer than
 
2640
            # the target.
 
2641
            graph = self.repository.get_graph(target.repository)
 
2642
            target.update_revisions(self, stop_revision,
 
2643
                overwrite=overwrite, graph=graph)
 
2644
        if self._push_should_merge_tags():
 
2645
            result.tag_conflicts = self.tags.merge_to(target.tags,
 
2646
                overwrite)
 
2647
        result.new_revno, result.new_revid = target.last_revision_info()
 
2648
        return result
 
2649
 
2587
2650
    def get_stacked_on_url(self):
2588
2651
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2589
2652
 
2600
2663
            self._transport.put_bytes('parent', url + '\n',
2601
2664
                mode=self.bzrdir._get_file_mode())
2602
2665
 
 
2666
 
 
2667
class BzrBranch5(BzrBranch):
 
2668
    """A format 5 branch. This supports new features over plain branches.
 
2669
 
 
2670
    It has support for a master_branch which is the data for bound branches.
 
2671
    """
 
2672
 
 
2673
    def get_bound_location(self):
 
2674
        try:
 
2675
            return self._transport.get_bytes('bound')[:-1]
 
2676
        except errors.NoSuchFile:
 
2677
            return None
 
2678
 
 
2679
    @needs_read_lock
 
2680
    def get_master_branch(self, possible_transports=None):
 
2681
        """Return the branch we are bound to.
 
2682
 
 
2683
        :return: Either a Branch, or None
 
2684
 
 
2685
        This could memoise the branch, but if thats done
 
2686
        it must be revalidated on each new lock.
 
2687
        So for now we just don't memoise it.
 
2688
        # RBC 20060304 review this decision.
 
2689
        """
 
2690
        bound_loc = self.get_bound_location()
 
2691
        if not bound_loc:
 
2692
            return None
 
2693
        try:
 
2694
            return Branch.open(bound_loc,
 
2695
                               possible_transports=possible_transports)
 
2696
        except (errors.NotBranchError, errors.ConnectionError), e:
 
2697
            raise errors.BoundBranchConnectionFailure(
 
2698
                    self, bound_loc, e)
 
2699
 
2603
2700
    @needs_write_lock
2604
 
    def unbind(self):
2605
 
        """If bound, unbind"""
2606
 
        return self.set_bound_location(None)
 
2701
    def set_bound_location(self, location):
 
2702
        """Set the target where this branch is bound to.
 
2703
 
 
2704
        :param location: URL to the target branch
 
2705
        """
 
2706
        if location:
 
2707
            self._transport.put_bytes('bound', location+'\n',
 
2708
                mode=self.bzrdir._get_file_mode())
 
2709
        else:
 
2710
            try:
 
2711
                self._transport.delete('bound')
 
2712
            except errors.NoSuchFile:
 
2713
                return False
 
2714
            return True
2607
2715
 
2608
2716
    @needs_write_lock
2609
2717
    def bind(self, other):
2631
2739
        # history around
2632
2740
        self.set_bound_location(other.base)
2633
2741
 
2634
 
    def get_bound_location(self):
2635
 
        try:
2636
 
            return self._transport.get_bytes('bound')[:-1]
2637
 
        except errors.NoSuchFile:
2638
 
            return None
2639
 
 
2640
 
    @needs_read_lock
2641
 
    def get_master_branch(self, possible_transports=None):
2642
 
        """Return the branch we are bound to.
2643
 
 
2644
 
        :return: Either a Branch, or None
2645
 
        """
2646
 
        if self._master_branch_cache is None:
2647
 
            self._master_branch_cache = self._get_master_branch(
2648
 
                possible_transports)
2649
 
        return self._master_branch_cache
2650
 
 
2651
 
    def _get_master_branch(self, possible_transports):
2652
 
        bound_loc = self.get_bound_location()
2653
 
        if not bound_loc:
2654
 
            return None
2655
 
        try:
2656
 
            return Branch.open(bound_loc,
2657
 
                               possible_transports=possible_transports)
2658
 
        except (errors.NotBranchError, errors.ConnectionError), e:
2659
 
            raise errors.BoundBranchConnectionFailure(
2660
 
                    self, bound_loc, e)
2661
 
 
2662
2742
    @needs_write_lock
2663
 
    def set_bound_location(self, location):
2664
 
        """Set the target where this branch is bound to.
2665
 
 
2666
 
        :param location: URL to the target branch
2667
 
        """
2668
 
        self._master_branch_cache = None
2669
 
        if location:
2670
 
            self._transport.put_bytes('bound', location+'\n',
2671
 
                mode=self.bzrdir._get_file_mode())
2672
 
        else:
2673
 
            try:
2674
 
                self._transport.delete('bound')
2675
 
            except errors.NoSuchFile:
2676
 
                return False
2677
 
            return True
 
2743
    def unbind(self):
 
2744
        """If bound, unbind"""
 
2745
        return self.set_bound_location(None)
2678
2746
 
2679
2747
    @needs_write_lock
2680
2748
    def update(self, possible_transports=None):
2693
2761
            return old_tip
2694
2762
        return None
2695
2763
 
2696
 
    def _read_last_revision_info(self):
2697
 
        revision_string = self._transport.get_bytes('last-revision')
2698
 
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2699
 
        revision_id = cache_utf8.get_cached_utf8(revision_id)
2700
 
        revno = int(revno)
2701
 
        return revno, revision_id
2702
 
 
2703
 
    def _write_last_revision_info(self, revno, revision_id):
2704
 
        """Simply write out the revision id, with no checks.
2705
 
 
2706
 
        Use set_last_revision_info to perform this safely.
2707
 
 
2708
 
        Does not update the revision_history cache.
2709
 
        """
2710
 
        revision_id = _mod_revision.ensure_null(revision_id)
2711
 
        out_string = '%d %s\n' % (revno, revision_id)
2712
 
        self._transport.put_bytes('last-revision', out_string,
2713
 
            mode=self.bzrdir._get_file_mode())
2714
 
 
2715
 
 
2716
 
class FullHistoryBzrBranch(BzrBranch):
2717
 
    """Bzr branch which contains the full revision history."""
2718
 
 
2719
 
    @needs_write_lock
2720
 
    def set_last_revision_info(self, revno, revision_id):
2721
 
        if not revision_id or not isinstance(revision_id, basestring):
2722
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2723
 
        revision_id = _mod_revision.ensure_null(revision_id)
2724
 
        # this old format stores the full history, but this api doesn't
2725
 
        # provide it, so we must generate, and might as well check it's
2726
 
        # correct
2727
 
        history = self._lefthand_history(revision_id)
2728
 
        if len(history) != revno:
2729
 
            raise AssertionError('%d != %d' % (len(history), revno))
2730
 
        self._set_revision_history(history)
2731
 
 
2732
 
    def _read_last_revision_info(self):
2733
 
        rh = self._revision_history()
2734
 
        revno = len(rh)
2735
 
        if revno:
2736
 
            return (revno, rh[-1])
2737
 
        else:
2738
 
            return (0, _mod_revision.NULL_REVISION)
2739
 
 
2740
 
    @deprecated_method(deprecated_in((2, 4, 0)))
2741
 
    @needs_write_lock
2742
 
    def set_revision_history(self, rev_history):
2743
 
        """See Branch.set_revision_history."""
2744
 
        self._set_revision_history(rev_history)
2745
 
 
2746
 
    def _set_revision_history(self, rev_history):
2747
 
        if 'evil' in debug.debug_flags:
2748
 
            mutter_callsite(3, "set_revision_history scales with history.")
2749
 
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2750
 
        for rev_id in rev_history:
2751
 
            check_not_reserved_id(rev_id)
2752
 
        if Branch.hooks['post_change_branch_tip']:
2753
 
            # Don't calculate the last_revision_info() if there are no hooks
2754
 
            # that will use it.
2755
 
            old_revno, old_revid = self.last_revision_info()
2756
 
        if len(rev_history) == 0:
2757
 
            revid = _mod_revision.NULL_REVISION
2758
 
        else:
2759
 
            revid = rev_history[-1]
2760
 
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2761
 
        self._write_revision_history(rev_history)
2762
 
        self._clear_cached_state()
2763
 
        self._cache_revision_history(rev_history)
2764
 
        for hook in Branch.hooks['set_rh']:
2765
 
            hook(self, rev_history)
2766
 
        if Branch.hooks['post_change_branch_tip']:
2767
 
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2768
 
 
2769
 
    def _write_revision_history(self, history):
2770
 
        """Factored out of set_revision_history.
2771
 
 
2772
 
        This performs the actual writing to disk.
2773
 
        It is intended to be called by set_revision_history."""
2774
 
        self._transport.put_bytes(
2775
 
            'revision-history', '\n'.join(history),
2776
 
            mode=self.bzrdir._get_file_mode())
2777
 
 
2778
 
    def _gen_revision_history(self):
2779
 
        history = self._transport.get_bytes('revision-history').split('\n')
2780
 
        if history[-1:] == ['']:
2781
 
            # There shouldn't be a trailing newline, but just in case.
2782
 
            history.pop()
2783
 
        return history
2784
 
 
2785
 
    def _synchronize_history(self, destination, revision_id):
2786
 
        if not isinstance(destination, FullHistoryBzrBranch):
2787
 
            super(BzrBranch, self)._synchronize_history(
2788
 
                destination, revision_id)
2789
 
            return
2790
 
        if revision_id == _mod_revision.NULL_REVISION:
2791
 
            new_history = []
2792
 
        else:
2793
 
            new_history = self._revision_history()
2794
 
        if revision_id is not None and new_history != []:
2795
 
            try:
2796
 
                new_history = new_history[:new_history.index(revision_id) + 1]
2797
 
            except ValueError:
2798
 
                rev = self.repository.get_revision(revision_id)
2799
 
                new_history = rev.get_history(self.repository)[1:]
2800
 
        destination._set_revision_history(new_history)
2801
 
 
2802
 
    @needs_write_lock
2803
 
    def generate_revision_history(self, revision_id, last_rev=None,
2804
 
        other_branch=None):
2805
 
        """Create a new revision history that will finish with revision_id.
2806
 
 
2807
 
        :param revision_id: the new tip to use.
2808
 
        :param last_rev: The previous last_revision. If not None, then this
2809
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
2810
 
        :param other_branch: The other branch that DivergedBranches should
2811
 
            raise with respect to.
2812
 
        """
2813
 
        self._set_revision_history(self._lefthand_history(revision_id,
2814
 
            last_rev, other_branch))
2815
 
 
2816
 
 
2817
 
class BzrBranch5(FullHistoryBzrBranch):
2818
 
    """A format 5 branch. This supports new features over plain branches.
2819
 
 
2820
 
    It has support for a master_branch which is the data for bound branches.
2821
 
    """
2822
 
 
2823
 
 
2824
 
class BzrBranch8(BzrBranch):
 
2764
 
 
2765
class BzrBranch8(BzrBranch5):
2825
2766
    """A branch that stores tree-reference locations."""
2826
2767
 
2827
 
    def _open_hook(self, possible_transports=None):
 
2768
    def _open_hook(self):
2828
2769
        if self._ignore_fallbacks:
2829
2770
            return
2830
 
        if possible_transports is None:
2831
 
            possible_transports = [self.bzrdir.root_transport]
2832
2771
        try:
2833
2772
            url = self.get_stacked_on_url()
2834
2773
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2842
2781
                    raise AssertionError(
2843
2782
                        "'transform_fallback_location' hook %s returned "
2844
2783
                        "None, not a URL." % hook_name)
2845
 
            self._activate_fallback_location(url,
2846
 
                possible_transports=possible_transports)
 
2784
            self._activate_fallback_location(url)
2847
2785
 
2848
2786
    def __init__(self, *args, **kwargs):
2849
2787
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2856
2794
        self._last_revision_info_cache = None
2857
2795
        self._reference_info = None
2858
2796
 
 
2797
    def _last_revision_info(self):
 
2798
        revision_string = self._transport.get_bytes('last-revision')
 
2799
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
 
2800
        revision_id = cache_utf8.get_cached_utf8(revision_id)
 
2801
        revno = int(revno)
 
2802
        return revno, revision_id
 
2803
 
 
2804
    def _write_last_revision_info(self, revno, revision_id):
 
2805
        """Simply write out the revision id, with no checks.
 
2806
 
 
2807
        Use set_last_revision_info to perform this safely.
 
2808
 
 
2809
        Does not update the revision_history cache.
 
2810
        Intended to be called by set_last_revision_info and
 
2811
        _write_revision_history.
 
2812
        """
 
2813
        revision_id = _mod_revision.ensure_null(revision_id)
 
2814
        out_string = '%d %s\n' % (revno, revision_id)
 
2815
        self._transport.put_bytes('last-revision', out_string,
 
2816
            mode=self.bzrdir._get_file_mode())
 
2817
 
 
2818
    @needs_write_lock
 
2819
    def set_last_revision_info(self, revno, revision_id):
 
2820
        revision_id = _mod_revision.ensure_null(revision_id)
 
2821
        old_revno, old_revid = self.last_revision_info()
 
2822
        if self._get_append_revisions_only():
 
2823
            self._check_history_violation(revision_id)
 
2824
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
 
2825
        self._write_last_revision_info(revno, revision_id)
 
2826
        self._clear_cached_state()
 
2827
        self._last_revision_info_cache = revno, revision_id
 
2828
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2829
 
 
2830
    def _synchronize_history(self, destination, revision_id):
 
2831
        """Synchronize last revision and revision history between branches.
 
2832
 
 
2833
        :see: Branch._synchronize_history
 
2834
        """
 
2835
        # XXX: The base Branch has a fast implementation of this method based
 
2836
        # on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
 
2837
        # that uses set_revision_history.  This class inherits from BzrBranch5,
 
2838
        # but wants the fast implementation, so it calls
 
2839
        # Branch._synchronize_history directly.
 
2840
        Branch._synchronize_history(self, destination, revision_id)
 
2841
 
2859
2842
    def _check_history_violation(self, revision_id):
2860
 
        current_revid = self.last_revision()
2861
 
        last_revision = _mod_revision.ensure_null(current_revid)
 
2843
        last_revision = _mod_revision.ensure_null(self.last_revision())
2862
2844
        if _mod_revision.is_null(last_revision):
2863
2845
            return
2864
 
        graph = self.repository.get_graph()
2865
 
        for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2866
 
            if lh_ancestor == current_revid:
2867
 
                return
2868
 
        raise errors.AppendRevisionsOnlyViolation(self.user_url)
 
2846
        if last_revision not in self._lefthand_history(revision_id):
 
2847
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
2869
2848
 
2870
2849
    def _gen_revision_history(self):
2871
2850
        """Generate the revision history from last revision
2874
2853
        self._extend_partial_history(stop_index=last_revno-1)
2875
2854
        return list(reversed(self._partial_revision_history_cache))
2876
2855
 
 
2856
    def _write_revision_history(self, history):
 
2857
        """Factored out of set_revision_history.
 
2858
 
 
2859
        This performs the actual writing to disk, with format-specific checks.
 
2860
        It is intended to be called by BzrBranch5.set_revision_history.
 
2861
        """
 
2862
        if len(history) == 0:
 
2863
            last_revision = 'null:'
 
2864
        else:
 
2865
            if history != self._lefthand_history(history[-1]):
 
2866
                raise errors.NotLefthandHistory(history)
 
2867
            last_revision = history[-1]
 
2868
        if self._get_append_revisions_only():
 
2869
            self._check_history_violation(last_revision)
 
2870
        self._write_last_revision_info(len(history), last_revision)
 
2871
 
2877
2872
    @needs_write_lock
2878
2873
    def _set_parent_location(self, url):
2879
2874
        """Set the parent branch"""
2965
2960
 
2966
2961
    def set_bound_location(self, location):
2967
2962
        """See Branch.set_push_location."""
2968
 
        self._master_branch_cache = None
2969
2963
        result = None
2970
 
        conf = self.get_config_stack()
 
2964
        config = self.get_config()
2971
2965
        if location is None:
2972
 
            if not conf.get('bound'):
 
2966
            if config.get_user_option('bound') != 'True':
2973
2967
                return False
2974
2968
            else:
2975
 
                conf.set('bound', 'False')
 
2969
                config.set_user_option('bound', 'False', warn_masked=True)
2976
2970
                return True
2977
2971
        else:
2978
2972
            self._set_config_location('bound_location', location,
2979
 
                                      config=conf)
2980
 
            conf.set('bound', 'True')
 
2973
                                      config=config)
 
2974
            config.set_user_option('bound', 'True', warn_masked=True)
2981
2975
        return True
2982
2976
 
2983
2977
    def _get_bound_location(self, bound):
2984
2978
        """Return the bound location in the config file.
2985
2979
 
2986
2980
        Return None if the bound parameter does not match"""
2987
 
        conf = self.get_config_stack()
2988
 
        if conf.get('bound') != bound:
 
2981
        config = self.get_config()
 
2982
        config_bound = (config.get_user_option('bound') == 'True')
 
2983
        if config_bound != bound:
2989
2984
            return None
2990
 
        return self._get_config_location('bound_location', config=conf)
 
2985
        return self._get_config_location('bound_location', config=config)
2991
2986
 
2992
2987
    def get_bound_location(self):
2993
2988
        """See Branch.set_push_location."""
3001
2996
        # you can always ask for the URL; but you might not be able to use it
3002
2997
        # if the repo can't support stacking.
3003
2998
        ## self._check_stackable_repo()
3004
 
        # stacked_on_location is only ever defined in branch.conf, so don't
3005
 
        # waste effort reading the whole stack of config files.
3006
 
        conf = _mod_config.BranchOnlyStack(self)
3007
 
        stacked_url = self._get_config_location('stacked_on_location',
3008
 
                                                config=conf)
 
2999
        stacked_url = self._get_config_location('stacked_on_location')
3009
3000
        if stacked_url is None:
3010
3001
            raise errors.NotStacked(self)
3011
3002
        return stacked_url
3012
3003
 
 
3004
    def _get_append_revisions_only(self):
 
3005
        return self.get_config(
 
3006
            ).get_user_option_as_bool('append_revisions_only')
 
3007
 
 
3008
    @needs_write_lock
 
3009
    def generate_revision_history(self, revision_id, last_rev=None,
 
3010
                                  other_branch=None):
 
3011
        """See BzrBranch5.generate_revision_history"""
 
3012
        history = self._lefthand_history(revision_id, last_rev, other_branch)
 
3013
        revno = len(history)
 
3014
        self.set_last_revision_info(revno, revision_id)
 
3015
 
3013
3016
    @needs_read_lock
3014
3017
    def get_rev_id(self, revno, history=None):
3015
3018
        """Find the revision id of the specified revno."""
3039
3042
        try:
3040
3043
            index = self._partial_revision_history_cache.index(revision_id)
3041
3044
        except ValueError:
3042
 
            try:
3043
 
                self._extend_partial_history(stop_revision=revision_id)
3044
 
            except errors.RevisionNotPresent, e:
3045
 
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
 
3045
            self._extend_partial_history(stop_revision=revision_id)
3046
3046
            index = len(self._partial_revision_history_cache) - 1
3047
 
            if index < 0:
3048
 
                raise errors.NoSuchRevision(self, revision_id)
3049
3047
            if self._partial_revision_history_cache[index] != revision_id:
3050
3048
                raise errors.NoSuchRevision(self, revision_id)
3051
3049
        return self.revno() - index
3103
3101
    :ivar local_branch: target branch if there is a Master, else None
3104
3102
    :ivar target_branch: Target/destination branch object. (write locked)
3105
3103
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
3106
 
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3107
3104
    """
3108
3105
 
3109
3106
    @deprecated_method(deprecated_in((2, 3, 0)))
3115
3112
        return self.new_revno - self.old_revno
3116
3113
 
3117
3114
    def report(self, to_file):
3118
 
        tag_conflicts = getattr(self, "tag_conflicts", None)
3119
 
        tag_updates = getattr(self, "tag_updates", None)
3120
3115
        if not is_quiet():
3121
 
            if self.old_revid != self.new_revid:
 
3116
            if self.old_revid == self.new_revid:
 
3117
                to_file.write('No revisions to pull.\n')
 
3118
            else:
3122
3119
                to_file.write('Now on revision %d.\n' % self.new_revno)
3123
 
            if tag_updates:
3124
 
                to_file.write('%d tag(s) updated.\n' % len(tag_updates))
3125
 
            if self.old_revid == self.new_revid and not tag_updates:
3126
 
                if not tag_conflicts:
3127
 
                    to_file.write('No revisions or tags to pull.\n')
3128
 
                else:
3129
 
                    to_file.write('No revisions to pull.\n')
3130
3120
        self._show_tag_conficts(to_file)
3131
3121
 
3132
3122
 
3158
3148
        return self.new_revno - self.old_revno
3159
3149
 
3160
3150
    def report(self, to_file):
3161
 
        # TODO: This function gets passed a to_file, but then
3162
 
        # ignores it and calls note() instead. This is also
3163
 
        # inconsistent with PullResult(), which writes to stdout.
3164
 
        # -- JRV20110901, bug #838853
3165
 
        tag_conflicts = getattr(self, "tag_conflicts", None)
3166
 
        tag_updates = getattr(self, "tag_updates", None)
3167
 
        if not is_quiet():
3168
 
            if self.old_revid != self.new_revid:
3169
 
                note(gettext('Pushed up to revision %d.') % self.new_revno)
3170
 
            if tag_updates:
3171
 
                note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
3172
 
            if self.old_revid == self.new_revid and not tag_updates:
3173
 
                if not tag_conflicts:
3174
 
                    note(gettext('No new revisions or tags to push.'))
3175
 
                else:
3176
 
                    note(gettext('No new revisions to push.'))
 
3151
        """Write a human-readable description of the result."""
 
3152
        if self.old_revid == self.new_revid:
 
3153
            note('No new revisions to push.')
 
3154
        else:
 
3155
            note('Pushed up to revision %d.' % self.new_revno)
3177
3156
        self._show_tag_conficts(to_file)
3178
3157
 
3179
3158
 
3193
3172
        :param verbose: Requests more detailed display of what was checked,
3194
3173
            if any.
3195
3174
        """
3196
 
        note(gettext('checked branch {0} format {1}').format(
3197
 
                                self.branch.user_url, self.branch._format))
 
3175
        note('checked branch %s format %s', self.branch.user_url,
 
3176
            self.branch._format)
3198
3177
        for error in self.errors:
3199
 
            note(gettext('found error:%s'), error)
 
3178
            note('found error:%s', error)
3200
3179
 
3201
3180
 
3202
3181
class Converter5to6(object):
3241
3220
 
3242
3221
 
3243
3222
class Converter7to8(object):
3244
 
    """Perform an in-place upgrade of format 7 to format 8"""
 
3223
    """Perform an in-place upgrade of format 6 to format 7"""
3245
3224
 
3246
3225
    def convert(self, branch):
3247
3226
        format = BzrBranchFormat8()
3250
3229
        branch._transport.put_bytes('format', format.get_format_string())
3251
3230
 
3252
3231
 
 
3232
def _run_with_write_locked_target(target, callable, *args, **kwargs):
 
3233
    """Run ``callable(*args, **kwargs)``, write-locking target for the
 
3234
    duration.
 
3235
 
 
3236
    _run_with_write_locked_target will attempt to release the lock it acquires.
 
3237
 
 
3238
    If an exception is raised by callable, then that exception *will* be
 
3239
    propagated, even if the unlock attempt raises its own error.  Thus
 
3240
    _run_with_write_locked_target should be preferred to simply doing::
 
3241
 
 
3242
        target.lock_write()
 
3243
        try:
 
3244
            return callable(*args, **kwargs)
 
3245
        finally:
 
3246
            target.unlock()
 
3247
 
 
3248
    """
 
3249
    # This is very similar to bzrlib.decorators.needs_write_lock.  Perhaps they
 
3250
    # should share code?
 
3251
    target.lock_write()
 
3252
    try:
 
3253
        result = callable(*args, **kwargs)
 
3254
    except:
 
3255
        exc_info = sys.exc_info()
 
3256
        try:
 
3257
            target.unlock()
 
3258
        finally:
 
3259
            raise exc_info[0], exc_info[1], exc_info[2]
 
3260
    else:
 
3261
        target.unlock()
 
3262
        return result
 
3263
 
 
3264
 
3253
3265
class InterBranch(InterObject):
3254
3266
    """This class represents operations taking place between two branches.
3255
3267
 
3283
3295
        raise NotImplementedError(self.pull)
3284
3296
 
3285
3297
    @needs_write_lock
3286
 
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
3298
    def update_revisions(self, stop_revision=None, overwrite=False,
 
3299
                         graph=None):
 
3300
        """Pull in new perfect-fit revisions.
 
3301
 
 
3302
        :param stop_revision: Updated until the given revision
 
3303
        :param overwrite: Always set the branch pointer, rather than checking
 
3304
            to see if it is a proper descendant.
 
3305
        :param graph: A Graph object that can be used to query history
 
3306
            information. This can be None.
 
3307
        :return: None
 
3308
        """
 
3309
        raise NotImplementedError(self.update_revisions)
 
3310
 
 
3311
    @needs_write_lock
 
3312
    def push(self, overwrite=False, stop_revision=None,
3287
3313
             _override_hook_source_branch=None):
3288
3314
        """Mirror the source branch into the target branch.
3289
3315
 
3300
3326
        """
3301
3327
        raise NotImplementedError(self.copy_content_into)
3302
3328
 
3303
 
    @needs_write_lock
3304
 
    def fetch(self, stop_revision=None, limit=None):
3305
 
        """Fetch revisions.
3306
 
 
3307
 
        :param stop_revision: Last revision to fetch
3308
 
        :param limit: Optional rough limit of revisions to fetch
3309
 
        """
3310
 
        raise NotImplementedError(self.fetch)
3311
 
 
3312
3329
 
3313
3330
class GenericInterBranch(InterBranch):
3314
3331
    """InterBranch implementation that uses public Branch functions."""
3320
3337
 
3321
3338
    @classmethod
3322
3339
    def _get_branch_formats_to_test(klass):
3323
 
        return [(format_registry.get_default(), format_registry.get_default())]
 
3340
        return [(BranchFormat._default_format, BranchFormat._default_format)]
3324
3341
 
3325
3342
    @classmethod
3326
3343
    def unwrap_format(klass, format):
3327
3344
        if isinstance(format, remote.RemoteBranchFormat):
3328
3345
            format._ensure_real()
3329
3346
            return format._custom_format
3330
 
        return format
 
3347
        return format                                                                                                  
3331
3348
 
3332
3349
    @needs_write_lock
3333
3350
    def copy_content_into(self, revision_id=None):
3349
3366
            self.source.tags.merge_to(self.target.tags)
3350
3367
 
3351
3368
    @needs_write_lock
3352
 
    def fetch(self, stop_revision=None, limit=None):
3353
 
        if self.target.base == self.source.base:
3354
 
            return (0, [])
3355
 
        self.source.lock_read()
3356
 
        try:
3357
 
            fetch_spec_factory = fetch.FetchSpecFactory()
3358
 
            fetch_spec_factory.source_branch = self.source
3359
 
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3360
 
            fetch_spec_factory.source_repo = self.source.repository
3361
 
            fetch_spec_factory.target_repo = self.target.repository
3362
 
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3363
 
            fetch_spec_factory.limit = limit
3364
 
            fetch_spec = fetch_spec_factory.make_fetch_spec()
3365
 
            return self.target.repository.fetch(self.source.repository,
3366
 
                fetch_spec=fetch_spec)
3367
 
        finally:
3368
 
            self.source.unlock()
3369
 
 
3370
 
    @needs_write_lock
3371
 
    def _update_revisions(self, stop_revision=None, overwrite=False,
3372
 
            graph=None):
 
3369
    def update_revisions(self, stop_revision=None, overwrite=False,
 
3370
        graph=None):
 
3371
        """See InterBranch.update_revisions()."""
3373
3372
        other_revno, other_last_revision = self.source.last_revision_info()
3374
3373
        stop_revno = None # unknown
3375
3374
        if stop_revision is None:
3386
3385
        # case of having something to pull, and so that the check for
3387
3386
        # already merged can operate on the just fetched graph, which will
3388
3387
        # be cached in memory.
3389
 
        self.fetch(stop_revision=stop_revision)
 
3388
        self.target.fetch(self.source, stop_revision)
3390
3389
        # Check to see if one is an ancestor of the other
3391
3390
        if not overwrite:
3392
3391
            if graph is None:
3420
3419
        if local and not bound_location:
3421
3420
            raise errors.LocalRequiresBoundBranch()
3422
3421
        master_branch = None
3423
 
        source_is_master = False
3424
 
        if bound_location:
3425
 
            # bound_location comes from a config file, some care has to be
3426
 
            # taken to relate it to source.user_url
3427
 
            normalized = urlutils.normalize_url(bound_location)
3428
 
            try:
3429
 
                relpath = self.source.user_transport.relpath(normalized)
3430
 
                source_is_master = (relpath == '')
3431
 
            except (errors.PathNotChild, errors.InvalidURL):
3432
 
                source_is_master = False
3433
 
        if not local and bound_location and not source_is_master:
 
3422
        if not local and bound_location and self.source.user_url != bound_location:
3434
3423
            # not pulling from master, so we need to update master.
3435
3424
            master_branch = self.target.get_master_branch(possible_transports)
3436
3425
            master_branch.lock_write()
3442
3431
            return self._pull(overwrite,
3443
3432
                stop_revision, _hook_master=master_branch,
3444
3433
                run_hooks=run_hooks,
3445
 
                _override_hook_target=_override_hook_target,
3446
 
                merge_tags_to_master=not source_is_master)
 
3434
                _override_hook_target=_override_hook_target)
3447
3435
        finally:
3448
3436
            if master_branch:
3449
3437
                master_branch.unlock()
3450
3438
 
3451
 
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
3439
    def push(self, overwrite=False, stop_revision=None,
3452
3440
             _override_hook_source_branch=None):
3453
3441
        """See InterBranch.push.
3454
3442
 
3455
3443
        This is the basic concrete implementation of push()
3456
3444
 
3457
 
        :param _override_hook_source_branch: If specified, run the hooks
3458
 
            passing this Branch as the source, rather than self.  This is for
3459
 
            use of RemoteBranch, where push is delegated to the underlying
3460
 
            vfs-based Branch.
 
3445
        :param _override_hook_source_branch: If specified, run
 
3446
        the hooks passing this Branch as the source, rather than self.
 
3447
        This is for use of RemoteBranch, where push is delegated to the
 
3448
        underlying vfs-based Branch.
3461
3449
        """
3462
 
        if lossy:
3463
 
            raise errors.LossyPushToSameVCS(self.source, self.target)
3464
3450
        # TODO: Public option to disable running hooks - should be trivial but
3465
3451
        # needs tests.
3466
 
 
3467
 
        op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3468
 
        op.add_cleanup(self.source.lock_read().unlock)
3469
 
        op.add_cleanup(self.target.lock_write().unlock)
3470
 
        return op.run(overwrite, stop_revision,
3471
 
            _override_hook_source_branch=_override_hook_source_branch)
3472
 
 
3473
 
    def _basic_push(self, overwrite, stop_revision):
3474
 
        """Basic implementation of push without bound branches or hooks.
3475
 
 
3476
 
        Must be called with source read locked and target write locked.
3477
 
        """
3478
 
        result = BranchPushResult()
3479
 
        result.source_branch = self.source
3480
 
        result.target_branch = self.target
3481
 
        result.old_revno, result.old_revid = self.target.last_revision_info()
3482
 
        self.source.update_references(self.target)
3483
 
        if result.old_revid != stop_revision:
3484
 
            # We assume that during 'push' this repository is closer than
3485
 
            # the target.
3486
 
            graph = self.source.repository.get_graph(self.target.repository)
3487
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3488
 
                    graph=graph)
3489
 
        if self.source._push_should_merge_tags():
3490
 
            result.tag_updates, result.tag_conflicts = (
3491
 
                self.source.tags.merge_to(self.target.tags, overwrite))
3492
 
        result.new_revno, result.new_revid = self.target.last_revision_info()
3493
 
        return result
3494
 
 
3495
 
    def _push_with_bound_branches(self, operation, overwrite, stop_revision,
 
3452
        self.source.lock_read()
 
3453
        try:
 
3454
            return _run_with_write_locked_target(
 
3455
                self.target, self._push_with_bound_branches, overwrite,
 
3456
                stop_revision,
 
3457
                _override_hook_source_branch=_override_hook_source_branch)
 
3458
        finally:
 
3459
            self.source.unlock()
 
3460
 
 
3461
    def _push_with_bound_branches(self, overwrite, stop_revision,
3496
3462
            _override_hook_source_branch=None):
3497
3463
        """Push from source into target, and into target's master if any.
3498
3464
        """
3510
3476
            # be bound to itself? -- mbp 20070507
3511
3477
            master_branch = self.target.get_master_branch()
3512
3478
            master_branch.lock_write()
3513
 
            operation.add_cleanup(master_branch.unlock)
3514
 
            # push into the master from the source branch.
3515
 
            master_inter = InterBranch.get(self.source, master_branch)
3516
 
            master_inter._basic_push(overwrite, stop_revision)
3517
 
            # and push into the target branch from the source. Note that
3518
 
            # we push from the source branch again, because it's considered
3519
 
            # the highest bandwidth repository.
3520
 
            result = self._basic_push(overwrite, stop_revision)
3521
 
            result.master_branch = master_branch
3522
 
            result.local_branch = self.target
 
3479
            try:
 
3480
                # push into the master from the source branch.
 
3481
                self.source._basic_push(master_branch, overwrite, stop_revision)
 
3482
                # and push into the target branch from the source. Note that we
 
3483
                # push from the source branch again, because it's considered the
 
3484
                # highest bandwidth repository.
 
3485
                result = self.source._basic_push(self.target, overwrite,
 
3486
                    stop_revision)
 
3487
                result.master_branch = master_branch
 
3488
                result.local_branch = self.target
 
3489
                _run_hooks()
 
3490
                return result
 
3491
            finally:
 
3492
                master_branch.unlock()
3523
3493
        else:
3524
 
            master_branch = None
3525
3494
            # no master branch
3526
 
            result = self._basic_push(overwrite, stop_revision)
 
3495
            result = self.source._basic_push(self.target, overwrite,
 
3496
                stop_revision)
3527
3497
            # TODO: Why set master_branch and local_branch if there's no
3528
3498
            # binding?  Maybe cleaner to just leave them unset? -- mbp
3529
3499
            # 20070504
3530
3500
            result.master_branch = self.target
3531
3501
            result.local_branch = None
3532
 
        _run_hooks()
3533
 
        return result
 
3502
            _run_hooks()
 
3503
            return result
3534
3504
 
3535
3505
    def _pull(self, overwrite=False, stop_revision=None,
3536
3506
             possible_transports=None, _hook_master=None, run_hooks=True,
3537
 
             _override_hook_target=None, local=False,
3538
 
             merge_tags_to_master=True):
 
3507
             _override_hook_target=None, local=False):
3539
3508
        """See Branch.pull.
3540
3509
 
3541
3510
        This function is the core worker, used by GenericInterBranch.pull to
3546
3515
        :param run_hooks: Private parameter - if false, this branch
3547
3516
            is being called because it's the master of the primary branch,
3548
3517
            so it should not run its hooks.
3549
 
            is being called because it's the master of the primary branch,
3550
 
            so it should not run its hooks.
3551
3518
        :param _override_hook_target: Private parameter - set the branch to be
3552
3519
            supplied as the target_branch to pull hooks.
3553
3520
        :param local: Only update the local branch, and not the bound branch.
3572
3539
            # -- JRV20090506
3573
3540
            result.old_revno, result.old_revid = \
3574
3541
                self.target.last_revision_info()
3575
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3576
 
                graph=graph)
 
3542
            self.target.update_revisions(self.source, stop_revision,
 
3543
                overwrite=overwrite, graph=graph)
3577
3544
            # TODO: The old revid should be specified when merging tags, 
3578
3545
            # so a tags implementation that versions tags can only 
3579
3546
            # pull in the most recent changes. -- JRV20090506
3580
 
            result.tag_updates, result.tag_conflicts = (
3581
 
                self.source.tags.merge_to(self.target.tags, overwrite,
3582
 
                    ignore_master=not merge_tags_to_master))
 
3547
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3548
                overwrite)
3583
3549
            result.new_revno, result.new_revid = self.target.last_revision_info()
3584
3550
            if _hook_master:
3585
3551
                result.master_branch = _hook_master