~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Vincent Ladeuil
  • Date: 2010-12-07 10:16:53 UTC
  • mto: (5575.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 5576.
  • Revision ID: v.ladeuil+lp@free.fr-20101207101653-20iiufih26buvmy3
Use assertLength as it provides a better ouput to debug tests.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2012 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
 
    bzrdir,
57
 
    controldir,
58
 
    )
59
 
from bzrlib.decorators import (
60
 
    needs_read_lock,
61
 
    needs_write_lock,
62
 
    only_raises,
63
 
    )
64
 
from bzrlib.hooks import Hooks
 
51
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
 
52
from bzrlib.hooks import HookPoint, Hooks
65
53
from bzrlib.inter import InterObject
66
54
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
67
55
from bzrlib import registry
72
60
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
73
61
 
74
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
 
75
68
class Branch(controldir.ControlComponent):
76
69
    """Branch holding a history of revisions.
77
70
 
78
71
    :ivar base:
79
72
        Base directory/url of the branch; using control_url and
80
73
        control_transport is more standardized.
81
 
    :ivar hooks: An instance of BranchHooks.
82
 
    :ivar _master_branch_cache: cached result of get_master_branch, see
83
 
        _clear_cached_state.
 
74
 
 
75
    hooks: An instance of BranchHooks.
84
76
    """
85
77
    # this is really an instance variable - FIXME move it there
86
78
    # - RBC 20060112
94
86
    def user_transport(self):
95
87
        return self.bzrdir.user_transport
96
88
 
97
 
    def __init__(self, possible_transports=None):
 
89
    def __init__(self, *ignored, **ignored_too):
98
90
        self.tags = self._format.make_tags(self)
99
91
        self._revision_history_cache = None
100
92
        self._revision_id_to_revno_cache = None
102
94
        self._partial_revision_history_cache = []
103
95
        self._tags_bytes = None
104
96
        self._last_revision_info_cache = None
105
 
        self._master_branch_cache = None
106
97
        self._merge_sorted_revisions_cache = None
107
 
        self._open_hook(possible_transports)
 
98
        self._open_hook()
108
99
        hooks = Branch.hooks['open']
109
100
        for hook in hooks:
110
101
            hook(self)
111
102
 
112
 
    def _open_hook(self, possible_transports):
 
103
    def _open_hook(self):
113
104
        """Called by init to allow simpler extension of the base class."""
114
105
 
115
 
    def _activate_fallback_location(self, url, possible_transports):
 
106
    def _activate_fallback_location(self, url):
116
107
        """Activate the branch/repository from url as a fallback repository."""
117
 
        for existing_fallback_repo in self.repository._fallback_repositories:
118
 
            if existing_fallback_repo.user_url == url:
119
 
                # This fallback is already configured.  This probably only
120
 
                # happens because ControlDir.sprout is a horrible mess.  To avoid
121
 
                # confusing _unstack we don't add this a second time.
122
 
                mutter('duplicate activation of fallback %r on %r', url, self)
123
 
                return
124
 
        repo = self._get_fallback_repository(url, possible_transports)
 
108
        repo = self._get_fallback_repository(url)
125
109
        if repo.has_same_location(self.repository):
126
110
            raise errors.UnstackableLocationError(self.user_url, url)
127
111
        self.repository.add_fallback_repository(repo)
181
165
        For instance, if the branch is at URL/.bzr/branch,
182
166
        Branch.open(URL) -> a Branch instance.
183
167
        """
184
 
        control = controldir.ControlDir.open(base,
185
 
            possible_transports=possible_transports, _unsupported=_unsupported)
186
 
        return control.open_branch(unsupported=_unsupported,
187
 
            possible_transports=possible_transports)
 
168
        control = bzrdir.BzrDir.open(base, _unsupported,
 
169
                                     possible_transports=possible_transports)
 
170
        return control.open_branch(unsupported=_unsupported)
188
171
 
189
172
    @staticmethod
190
 
    def open_from_transport(transport, name=None, _unsupported=False,
191
 
            possible_transports=None):
 
173
    def open_from_transport(transport, name=None, _unsupported=False):
192
174
        """Open the branch rooted at transport"""
193
 
        control = controldir.ControlDir.open_from_transport(transport, _unsupported)
194
 
        return control.open_branch(name=name, unsupported=_unsupported,
195
 
            possible_transports=possible_transports)
 
175
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
 
176
        return control.open_branch(name=name, unsupported=_unsupported)
196
177
 
197
178
    @staticmethod
198
179
    def open_containing(url, possible_transports=None):
206
187
        format, UnknownFormatError or UnsupportedFormatError are raised.
207
188
        If there is one, it is returned, along with the unused portion of url.
208
189
        """
209
 
        control, relpath = controldir.ControlDir.open_containing(url,
 
190
        control, relpath = bzrdir.BzrDir.open_containing(url,
210
191
                                                         possible_transports)
211
 
        branch = control.open_branch(possible_transports=possible_transports)
212
 
        return (branch, relpath)
 
192
        return control.open_branch(), relpath
213
193
 
214
194
    def _push_should_merge_tags(self):
215
195
        """Should _basic_push merge this branch's tags into the target?
227
207
 
228
208
        :return: A bzrlib.config.BranchConfig.
229
209
        """
230
 
        return _mod_config.BranchConfig(self)
231
 
 
232
 
    def get_config_stack(self):
233
 
        """Get a bzrlib.config.BranchStack for this Branch.
234
 
 
235
 
        This can then be used to get and set configuration options for the
236
 
        branch.
237
 
 
238
 
        :return: A bzrlib.config.BranchStack.
239
 
        """
240
 
        return _mod_config.BranchStack(self)
 
210
        return BranchConfig(self)
241
211
 
242
212
    def _get_config(self):
243
213
        """Get the concrete config for just the config in this branch.
251
221
        """
252
222
        raise NotImplementedError(self._get_config)
253
223
 
254
 
    def _get_fallback_repository(self, url, possible_transports):
 
224
    def _get_fallback_repository(self, url):
255
225
        """Get the repository we fallback to at url."""
256
226
        url = urlutils.join(self.base, url)
257
 
        a_branch = Branch.open(url, possible_transports=possible_transports)
 
227
        a_branch = Branch.open(url,
 
228
            possible_transports=[self.bzrdir.root_transport])
258
229
        return a_branch.repository
259
230
 
260
231
    @needs_read_lock
469
440
            after. If None, the rest of history is included.
470
441
        :param stop_rule: if stop_revision_id is not None, the precise rule
471
442
            to use for termination:
472
 
 
473
443
            * 'exclude' - leave the stop revision out of the result (default)
474
444
            * 'include' - the stop revision is the last item in the result
475
445
            * 'with-merges' - include the stop revision and all of its
477
447
            * 'with-merges-without-common-ancestry' - filter out revisions 
478
448
              that are in both ancestries
479
449
        :param direction: either 'reverse' or 'forward':
480
 
 
481
450
            * reverse means return the start_revision_id first, i.e.
482
451
              start at the most recent revision and go backwards in history
483
452
            * forward returns tuples in the opposite order to reverse.
527
496
        rev_iter = iter(merge_sorted_revisions)
528
497
        if start_revision_id is not None:
529
498
            for node in rev_iter:
530
 
                rev_id = node.key
 
499
                rev_id = node.key[-1]
531
500
                if rev_id != start_revision_id:
532
501
                    continue
533
502
                else:
534
503
                    # The decision to include the start or not
535
504
                    # depends on the stop_rule if a stop is provided
536
505
                    # so pop this node back into the iterator
537
 
                    rev_iter = itertools.chain(iter([node]), rev_iter)
 
506
                    rev_iter = chain(iter([node]), rev_iter)
538
507
                    break
539
508
        if stop_revision_id is None:
540
509
            # Yield everything
541
510
            for node in rev_iter:
542
 
                rev_id = node.key
 
511
                rev_id = node.key[-1]
543
512
                yield (rev_id, node.merge_depth, node.revno,
544
513
                       node.end_of_merge)
545
514
        elif stop_rule == 'exclude':
546
515
            for node in rev_iter:
547
 
                rev_id = node.key
 
516
                rev_id = node.key[-1]
548
517
                if rev_id == stop_revision_id:
549
518
                    return
550
519
                yield (rev_id, node.merge_depth, node.revno,
551
520
                       node.end_of_merge)
552
521
        elif stop_rule == 'include':
553
522
            for node in rev_iter:
554
 
                rev_id = node.key
 
523
                rev_id = node.key[-1]
555
524
                yield (rev_id, node.merge_depth, node.revno,
556
525
                       node.end_of_merge)
557
526
                if rev_id == stop_revision_id:
563
532
            ancestors = graph.find_unique_ancestors(start_revision_id,
564
533
                                                    [stop_revision_id])
565
534
            for node in rev_iter:
566
 
                rev_id = node.key
 
535
                rev_id = node.key[-1]
567
536
                if rev_id not in ancestors:
568
537
                    continue
569
538
                yield (rev_id, node.merge_depth, node.revno,
579
548
            reached_stop_revision_id = False
580
549
            revision_id_whitelist = []
581
550
            for node in rev_iter:
582
 
                rev_id = node.key
 
551
                rev_id = node.key[-1]
583
552
                if rev_id == left_parent:
584
553
                    # reached the left parent after the stop_revision
585
554
                    return
665
634
        """
666
635
        raise errors.UpgradeRequired(self.user_url)
667
636
 
668
 
    def get_append_revisions_only(self):
669
 
        """Whether it is only possible to append revisions to the history.
670
 
        """
671
 
        if not self._format.supports_set_append_revisions_only():
672
 
            return False
673
 
        return self.get_config_stack().get('append_revisions_only')
674
 
 
675
637
    def set_append_revisions_only(self, enabled):
676
638
        if not self._format.supports_set_append_revisions_only():
677
639
            raise errors.UpgradeRequired(self.user_url)
678
 
        self.get_config_stack().set('append_revisions_only', enabled)
 
640
        if enabled:
 
641
            value = 'True'
 
642
        else:
 
643
            value = 'False'
 
644
        self.get_config().set_user_option('append_revisions_only', value,
 
645
            warn_masked=True)
679
646
 
680
647
    def set_reference_info(self, file_id, tree_path, branch_location):
681
648
        """Set the branch location to use for a tree reference."""
686
653
        raise errors.UnsupportedOperation(self.get_reference_info, self)
687
654
 
688
655
    @needs_write_lock
689
 
    def fetch(self, from_branch, last_revision=None, limit=None):
 
656
    def fetch(self, from_branch, last_revision=None, pb=None):
690
657
        """Copy revisions from from_branch into this branch.
691
658
 
692
659
        :param from_branch: Where to copy from.
693
660
        :param last_revision: What revision to stop at (None for at the end
694
661
                              of the branch.
695
 
        :param limit: Optional rough limit of revisions to fetch
 
662
        :param pb: An optional progress bar to use.
696
663
        :return: None
697
664
        """
698
 
        return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
 
665
        if self.base == from_branch.base:
 
666
            return (0, [])
 
667
        if pb is not None:
 
668
            symbol_versioning.warn(
 
669
                symbol_versioning.deprecated_in((1, 14, 0))
 
670
                % "pb parameter to fetch()")
 
671
        from_branch.lock_read()
 
672
        try:
 
673
            if last_revision is None:
 
674
                last_revision = from_branch.last_revision()
 
675
                last_revision = _mod_revision.ensure_null(last_revision)
 
676
            return self.repository.fetch(from_branch.repository,
 
677
                                         revision_id=last_revision,
 
678
                                         pb=pb)
 
679
        finally:
 
680
            from_branch.unlock()
699
681
 
700
682
    def get_bound_location(self):
701
683
        """Return the URL of the branch we are bound to.
710
692
        """
711
693
        raise errors.UpgradeRequired(self.user_url)
712
694
 
713
 
    def get_commit_builder(self, parents, config_stack=None, timestamp=None,
 
695
    def get_commit_builder(self, parents, config=None, timestamp=None,
714
696
                           timezone=None, committer=None, revprops=None,
715
 
                           revision_id=None, lossy=False):
 
697
                           revision_id=None):
716
698
        """Obtain a CommitBuilder for this branch.
717
699
 
718
700
        :param parents: Revision ids of the parents of the new revision.
722
704
        :param committer: Optional committer to set for commit.
723
705
        :param revprops: Optional dictionary of revision properties.
724
706
        :param revision_id: Optional revision id.
725
 
        :param lossy: Whether to discard data that can not be natively
726
 
            represented, when pushing to a foreign VCS 
727
707
        """
728
708
 
729
 
        if config_stack is None:
730
 
            config_stack = self.get_config_stack()
 
709
        if config is None:
 
710
            config = self.get_config()
731
711
 
732
 
        return self.repository.get_commit_builder(self, parents, config_stack,
733
 
            timestamp, timezone, committer, revprops, revision_id,
734
 
            lossy)
 
712
        return self.repository.get_commit_builder(self, parents, config,
 
713
            timestamp, timezone, committer, revprops, revision_id)
735
714
 
736
715
    def get_master_branch(self, possible_transports=None):
737
716
        """Return the branch we are bound to.
740
719
        """
741
720
        return None
742
721
 
743
 
    @deprecated_method(deprecated_in((2, 5, 0)))
744
722
    def get_revision_delta(self, revno):
745
723
        """Return the delta for one revision.
746
724
 
747
725
        The delta is relative to its mainline predecessor, or the
748
726
        empty tree for revision 1.
749
727
        """
750
 
        try:
751
 
            revid = self.get_rev_id(revno)
752
 
        except errors.NoSuchRevision:
 
728
        rh = self.revision_history()
 
729
        if not (1 <= revno <= len(rh)):
753
730
            raise errors.InvalidRevisionNumber(revno)
754
 
        return self.repository.get_revision_delta(revid)
 
731
        return self.repository.get_revision_delta(rh[revno-1])
755
732
 
756
733
    def get_stacked_on_url(self):
757
734
        """Get the URL this branch is stacked against.
766
743
        """Print `file` to stdout."""
767
744
        raise NotImplementedError(self.print_file)
768
745
 
769
 
    @needs_write_lock
770
 
    def set_last_revision_info(self, revno, revision_id):
771
 
        """Set the last revision of this branch.
772
 
 
773
 
        The caller is responsible for checking that the revno is correct
774
 
        for this revision id.
775
 
 
776
 
        It may be possible to set the branch last revision to an id not
777
 
        present in the repository.  However, branches can also be
778
 
        configured to check constraints on history, in which case this may not
779
 
        be permitted.
780
 
        """
781
 
        raise NotImplementedError(self.set_last_revision_info)
782
 
 
783
 
    @needs_write_lock
784
 
    def generate_revision_history(self, revision_id, last_rev=None,
785
 
                                  other_branch=None):
786
 
        """See Branch.generate_revision_history"""
787
 
        graph = self.repository.get_graph()
788
 
        (last_revno, last_revid) = self.last_revision_info()
789
 
        known_revision_ids = [
790
 
            (last_revid, last_revno),
791
 
            (_mod_revision.NULL_REVISION, 0),
792
 
            ]
793
 
        if last_rev is not None:
794
 
            if not graph.is_ancestor(last_rev, revision_id):
795
 
                # our previous tip is not merged into stop_revision
796
 
                raise errors.DivergedBranches(self, other_branch)
797
 
        revno = graph.find_distance_to_null(revision_id, known_revision_ids)
798
 
        self.set_last_revision_info(revno, revision_id)
 
746
    def set_revision_history(self, rev_history):
 
747
        raise NotImplementedError(self.set_revision_history)
799
748
 
800
749
    @needs_write_lock
801
750
    def set_parent(self, url):
838
787
                return
839
788
            self._unstack()
840
789
        else:
841
 
            self._activate_fallback_location(url,
842
 
                possible_transports=[self.bzrdir.root_transport])
 
790
            self._activate_fallback_location(url)
843
791
        # write this out after the repository is stacked to avoid setting a
844
792
        # stacked config that doesn't work.
845
793
        self._set_config_location('stacked_on_location', url)
846
794
 
847
795
    def _unstack(self):
848
796
        """Change a branch to be unstacked, copying data as needed.
849
 
 
 
797
        
850
798
        Don't call this directly, use set_stacked_on_url(None).
851
799
        """
852
800
        pb = ui.ui_factory.nested_progress_bar()
853
801
        try:
854
 
            pb.update(gettext("Unstacking"))
 
802
            pb.update("Unstacking")
855
803
            # The basic approach here is to fetch the tip of the branch,
856
804
            # including all available ghosts, from the existing stacked
857
805
            # repository into a new repository object without the fallbacks. 
861
809
            old_repository = self.repository
862
810
            if len(old_repository._fallback_repositories) != 1:
863
811
                raise AssertionError("can't cope with fallback repositories "
864
 
                    "of %r (fallbacks: %r)" % (old_repository,
865
 
                        old_repository._fallback_repositories))
 
812
                    "of %r" % (self.repository,))
866
813
            # Open the new repository object.
867
814
            # Repositories don't offer an interface to remove fallback
868
815
            # repositories today; take the conceptually simpler option and just
871
818
            # stream from one of them to the other.  This does mean doing
872
819
            # separate SSH connection setup, but unstacking is not a
873
820
            # common operation so it's tolerable.
874
 
            new_bzrdir = controldir.ControlDir.open(
875
 
                self.bzrdir.root_transport.base)
 
821
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
876
822
            new_repository = new_bzrdir.find_repository()
877
823
            if new_repository._fallback_repositories:
878
824
                raise AssertionError("didn't expect %r to have "
917
863
                # XXX: If you unstack a branch while it has a working tree
918
864
                # with a pending merge, the pending-merged revisions will no
919
865
                # longer be present.  You can (probably) revert and remerge.
920
 
                try:
921
 
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
922
 
                except errors.TagsNotSupported:
923
 
                    tags_to_fetch = set()
924
 
                fetch_spec = vf_search.NotInOtherForRevs(self.repository,
925
 
                    old_repository, required_ids=[self.last_revision()],
926
 
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
927
 
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
 
866
                #
 
867
                # XXX: This only fetches up to the tip of the repository; it
 
868
                # doesn't bring across any tags.  That's fairly consistent
 
869
                # with how branch works, but perhaps not ideal.
 
870
                self.repository.fetch(old_repository,
 
871
                    revision_id=self.last_revision(),
 
872
                    find_ghosts=True)
928
873
            finally:
929
874
                old_repository.unlock()
930
875
        finally:
935
880
 
936
881
        :seealso: Branch._get_tags_bytes.
937
882
        """
938
 
        op = cleanup.OperationWithCleanups(self._set_tags_bytes_locked)
939
 
        op.add_cleanup(self.lock_write().unlock)
940
 
        return op.run_simple(bytes)
 
883
        return _run_with_write_locked_target(self, self._set_tags_bytes_locked,
 
884
                bytes)
941
885
 
942
886
    def _set_tags_bytes_locked(self, bytes):
943
887
        self._tags_bytes = bytes
968
912
        This means the next call to revision_history will need to call
969
913
        _gen_revision_history.
970
914
 
971
 
        This API is semi-public; it is only for use by subclasses, all other
972
 
        code should consider it to be private.
 
915
        This API is semi-public; it only for use by subclasses, all other code
 
916
        should consider it to be private.
973
917
        """
974
918
        self._revision_history_cache = None
975
919
        self._revision_id_to_revno_cache = None
976
920
        self._last_revision_info_cache = None
977
 
        self._master_branch_cache = None
978
921
        self._merge_sorted_revisions_cache = None
979
922
        self._partial_revision_history_cache = []
980
923
        self._partial_revision_id_to_revno_cache = {}
995
938
        """
996
939
        raise NotImplementedError(self._gen_revision_history)
997
940
 
998
 
    def _revision_history(self):
 
941
    @needs_read_lock
 
942
    def revision_history(self):
 
943
        """Return sequence of revision ids on this branch.
 
944
 
 
945
        This method will cache the revision history for as long as it is safe to
 
946
        do so.
 
947
        """
999
948
        if 'evil' in debug.debug_flags:
1000
949
            mutter_callsite(3, "revision_history scales with history.")
1001
950
        if self._revision_history_cache is not None:
1028
977
        :return: A tuple (revno, revision_id).
1029
978
        """
1030
979
        if self._last_revision_info_cache is None:
1031
 
            self._last_revision_info_cache = self._read_last_revision_info()
 
980
            self._last_revision_info_cache = self._last_revision_info()
1032
981
        return self._last_revision_info_cache
1033
982
 
1034
 
    def _read_last_revision_info(self):
1035
 
        raise NotImplementedError(self._read_last_revision_info)
1036
 
 
1037
 
    @deprecated_method(deprecated_in((2, 4, 0)))
 
983
    def _last_revision_info(self):
 
984
        rh = self.revision_history()
 
985
        revno = len(rh)
 
986
        if revno:
 
987
            return (revno, rh[-1])
 
988
        else:
 
989
            return (0, _mod_revision.NULL_REVISION)
 
990
 
 
991
    @deprecated_method(deprecated_in((1, 6, 0)))
 
992
    def missing_revisions(self, other, stop_revision=None):
 
993
        """Return a list of new revisions that would perfectly fit.
 
994
 
 
995
        If self and other have not diverged, return a list of the revisions
 
996
        present in other, but missing from self.
 
997
        """
 
998
        self_history = self.revision_history()
 
999
        self_len = len(self_history)
 
1000
        other_history = other.revision_history()
 
1001
        other_len = len(other_history)
 
1002
        common_index = min(self_len, other_len) -1
 
1003
        if common_index >= 0 and \
 
1004
            self_history[common_index] != other_history[common_index]:
 
1005
            raise errors.DivergedBranches(self, other)
 
1006
 
 
1007
        if stop_revision is None:
 
1008
            stop_revision = other_len
 
1009
        else:
 
1010
            if stop_revision > other_len:
 
1011
                raise errors.NoSuchRevision(self, stop_revision)
 
1012
        return other_history[self_len:stop_revision]
 
1013
 
 
1014
    def update_revisions(self, other, stop_revision=None, overwrite=False,
 
1015
                         graph=None):
 
1016
        """Pull in new perfect-fit revisions.
 
1017
 
 
1018
        :param other: Another Branch to pull from
 
1019
        :param stop_revision: Updated until the given revision
 
1020
        :param overwrite: Always set the branch pointer, rather than checking
 
1021
            to see if it is a proper descendant.
 
1022
        :param graph: A Graph object that can be used to query history
 
1023
            information. This can be None.
 
1024
        :return: None
 
1025
        """
 
1026
        return InterBranch.get(other, self).update_revisions(stop_revision,
 
1027
            overwrite, graph)
 
1028
 
1038
1029
    def import_last_revision_info(self, source_repo, revno, revid):
1039
1030
        """Set the last revision info, importing from another repo if necessary.
1040
1031
 
 
1032
        This is used by the bound branch code to upload a revision to
 
1033
        the master branch first before updating the tip of the local branch.
 
1034
 
1041
1035
        :param source_repo: Source repository to optionally fetch from
1042
1036
        :param revno: Revision number of the new tip
1043
1037
        :param revid: Revision id of the new tip
1046
1040
            self.repository.fetch(source_repo, revision_id=revid)
1047
1041
        self.set_last_revision_info(revno, revid)
1048
1042
 
1049
 
    def import_last_revision_info_and_tags(self, source, revno, revid,
1050
 
                                           lossy=False):
1051
 
        """Set the last revision info, importing from another repo if necessary.
1052
 
 
1053
 
        This is used by the bound branch code to upload a revision to
1054
 
        the master branch first before updating the tip of the local branch.
1055
 
        Revisions referenced by source's tags are also transferred.
1056
 
 
1057
 
        :param source: Source branch to optionally fetch from
1058
 
        :param revno: Revision number of the new tip
1059
 
        :param revid: Revision id of the new tip
1060
 
        :param lossy: Whether to discard metadata that can not be
1061
 
            natively represented
1062
 
        :return: Tuple with the new revision number and revision id
1063
 
            (should only be different from the arguments when lossy=True)
1064
 
        """
1065
 
        if not self.repository.has_same_location(source.repository):
1066
 
            self.fetch(source, revid)
1067
 
        self.set_last_revision_info(revno, revid)
1068
 
        return (revno, revid)
1069
 
 
1070
1043
    def revision_id_to_revno(self, revision_id):
1071
1044
        """Given a revision id, return its revno"""
1072
1045
        if _mod_revision.is_null(revision_id):
1073
1046
            return 0
1074
 
        history = self._revision_history()
 
1047
        history = self.revision_history()
1075
1048
        try:
1076
1049
            return history.index(revision_id) + 1
1077
1050
        except ValueError:
1104
1077
            stop_revision=stop_revision,
1105
1078
            possible_transports=possible_transports, *args, **kwargs)
1106
1079
 
1107
 
    def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1108
 
            *args, **kwargs):
 
1080
    def push(self, target, overwrite=False, stop_revision=None, *args,
 
1081
        **kwargs):
1109
1082
        """Mirror this branch into target.
1110
1083
 
1111
1084
        This branch is considered to be 'local', having low latency.
1112
1085
        """
1113
1086
        return InterBranch.get(self, target).push(overwrite, stop_revision,
1114
 
            lossy, *args, **kwargs)
 
1087
            *args, **kwargs)
 
1088
 
 
1089
    def lossy_push(self, target, stop_revision=None):
 
1090
        """Push deltas into another branch.
 
1091
 
 
1092
        :note: This does not, like push, retain the revision ids from 
 
1093
            the source branch and will, rather than adding bzr-specific 
 
1094
            metadata, push only those semantics of the revision that can be 
 
1095
            natively represented by this branch' VCS.
 
1096
 
 
1097
        :param target: Target branch
 
1098
        :param stop_revision: Revision to push, defaults to last revision.
 
1099
        :return: BranchPushResult with an extra member revidmap: 
 
1100
            A dictionary mapping revision ids from the target branch 
 
1101
            to new revision ids in the target branch, for each 
 
1102
            revision that was pushed.
 
1103
        """
 
1104
        inter = InterBranch.get(self, target)
 
1105
        lossy_push = getattr(inter, "lossy_push", None)
 
1106
        if lossy_push is None:
 
1107
            raise errors.LossyPushToSameVCS(self, target)
 
1108
        return lossy_push(stop_revision)
1115
1109
 
1116
1110
    def basis_tree(self):
1117
1111
        """Return `Tree` object for last revision."""
1142
1136
    def _set_config_location(self, name, url, config=None,
1143
1137
                             make_relative=False):
1144
1138
        if config is None:
1145
 
            config = self.get_config_stack()
 
1139
            config = self.get_config()
1146
1140
        if url is None:
1147
1141
            url = ''
1148
1142
        elif make_relative:
1149
1143
            url = urlutils.relative_url(self.base, url)
1150
 
        config.set(name, url)
 
1144
        config.set_user_option(name, url, warn_masked=True)
1151
1145
 
1152
1146
    def _get_config_location(self, name, config=None):
1153
1147
        if config is None:
1154
 
            config = self.get_config_stack()
1155
 
        location = config.get(name)
 
1148
            config = self.get_config()
 
1149
        location = config.get_user_option(name)
1156
1150
        if location == '':
1157
1151
            location = None
1158
1152
        return location
1159
1153
 
1160
1154
    def get_child_submit_format(self):
1161
1155
        """Return the preferred format of submissions to this branch."""
1162
 
        return self.get_config_stack().get('child_submit_format')
 
1156
        return self.get_config().get_user_option("child_submit_format")
1163
1157
 
1164
1158
    def get_submit_branch(self):
1165
1159
        """Return the submit location of the branch.
1168
1162
        pattern is that the user can override it by specifying a
1169
1163
        location.
1170
1164
        """
1171
 
        return self.get_config_stack().get('submit_branch')
 
1165
        return self.get_config().get_user_option('submit_branch')
1172
1166
 
1173
1167
    def set_submit_branch(self, location):
1174
1168
        """Return the submit location of the branch.
1177
1171
        pattern is that the user can override it by specifying a
1178
1172
        location.
1179
1173
        """
1180
 
        self.get_config_stack().set('submit_branch', location)
 
1174
        self.get_config().set_user_option('submit_branch', location,
 
1175
            warn_masked=True)
1181
1176
 
1182
1177
    def get_public_branch(self):
1183
1178
        """Return the public location of the branch.
1196
1191
        self._set_config_location('public_branch', location)
1197
1192
 
1198
1193
    def get_push_location(self):
1199
 
        """Return None or the location to push this branch to."""
1200
 
        return self.get_config_stack().get('push_location')
 
1194
        """Return the None or the location to push this branch to."""
 
1195
        push_loc = self.get_config().get_user_option('push_location')
 
1196
        return push_loc
1201
1197
 
1202
1198
    def set_push_location(self, location):
1203
1199
        """Set a new push location for this branch."""
1270
1266
        return result
1271
1267
 
1272
1268
    @needs_read_lock
1273
 
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
1274
 
            repository=None):
 
1269
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
1275
1270
        """Create a new line of development from the branch, into to_bzrdir.
1276
1271
 
1277
1272
        to_bzrdir controls the branch format.
1282
1277
        if (repository_policy is not None and
1283
1278
            repository_policy.requires_stacking()):
1284
1279
            to_bzrdir._format.require_stacking(_skip_repo=True)
1285
 
        result = to_bzrdir.create_branch(repository=repository)
 
1280
        result = to_bzrdir.create_branch()
1286
1281
        result.lock_write()
1287
1282
        try:
1288
1283
            if repository_policy is not None:
1289
1284
                repository_policy.configure_branch(result)
1290
1285
            self.copy_content_into(result, revision_id=revision_id)
1291
 
            master_url = self.get_bound_location()
1292
 
            if master_url is None:
1293
 
                result.set_parent(self.bzrdir.root_transport.base)
1294
 
            else:
1295
 
                result.set_parent(master_url)
 
1286
            result.set_parent(self.bzrdir.root_transport.base)
1296
1287
        finally:
1297
1288
            result.unlock()
1298
1289
        return result
1372
1363
        # TODO: We should probably also check that self.revision_history
1373
1364
        # matches the repository for older branch formats.
1374
1365
        # If looking for the code that cross-checks repository parents against
1375
 
        # the Graph.iter_lefthand_ancestry output, that is now a repository
 
1366
        # the iter_reverse_revision_history output, that is now a repository
1376
1367
        # specific check.
1377
1368
        return result
1378
1369
 
1379
 
    def _get_checkout_format(self, lightweight=False):
 
1370
    def _get_checkout_format(self):
1380
1371
        """Return the most suitable metadir for a checkout of this branch.
1381
1372
        Weaves are used if this branch's repository uses weaves.
1382
1373
        """
1383
 
        format = self.repository.bzrdir.checkout_metadir()
1384
 
        format.set_branch_format(self._format)
 
1374
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
 
1375
            from bzrlib.repofmt import weaverepo
 
1376
            format = bzrdir.BzrDirMetaFormat1()
 
1377
            format.repository_format = weaverepo.RepositoryFormat7()
 
1378
        else:
 
1379
            format = self.repository.bzrdir.checkout_metadir()
 
1380
            format.set_branch_format(self._format)
1385
1381
        return format
1386
1382
 
1387
1383
    def create_clone_on_transport(self, to_transport, revision_id=None,
1417
1413
        :param to_location: The url to produce the checkout at
1418
1414
        :param revision_id: The revision to check out
1419
1415
        :param lightweight: If True, produce a lightweight checkout, otherwise,
1420
 
            produce a bound branch (heavyweight checkout)
 
1416
        produce a bound branch (heavyweight checkout)
1421
1417
        :param accelerator_tree: A tree which can be used for retrieving file
1422
1418
            contents more quickly than the revision tree, i.e. a workingtree.
1423
1419
            The revision tree will be used for cases where accelerator_tree's
1428
1424
        """
1429
1425
        t = transport.get_transport(to_location)
1430
1426
        t.ensure_base()
1431
 
        format = self._get_checkout_format(lightweight=lightweight)
1432
 
        try:
 
1427
        if lightweight:
 
1428
            format = self._get_checkout_format()
1433
1429
            checkout = format.initialize_on_transport(t)
1434
 
        except errors.AlreadyControlDirError:
1435
 
            # It's fine if the control directory already exists,
1436
 
            # as long as there is no existing branch and working tree.
1437
 
            checkout = controldir.ControlDir.open_from_transport(t)
1438
 
            try:
1439
 
                checkout.open_branch()
1440
 
            except errors.NotBranchError:
1441
 
                pass
1442
 
            else:
1443
 
                raise errors.AlreadyControlDirError(t.base)
1444
 
            if checkout.control_transport.base == self.bzrdir.control_transport.base:
1445
 
                # When checking out to the same control directory,
1446
 
                # always create a lightweight checkout
1447
 
                lightweight = True
1448
 
 
1449
 
        if lightweight:
1450
 
            from_branch = checkout.set_branch_reference(target_branch=self)
 
1430
            from_branch = BranchReferenceFormat().initialize(checkout, 
 
1431
                target_branch=self)
1451
1432
        else:
1452
 
            policy = checkout.determine_repository_policy()
1453
 
            repo = policy.acquire_repository()[0]
1454
 
            checkout_branch = checkout.create_branch()
 
1433
            format = self._get_checkout_format()
 
1434
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
 
1435
                to_location, force_new_tree=False, format=format)
 
1436
            checkout = checkout_branch.bzrdir
1455
1437
            checkout_branch.bind(self)
1456
1438
            # pull up to the specified revision_id to set the initial
1457
1439
            # branch tip correctly, and seed it with history.
1458
1440
            checkout_branch.pull(self, stop_revision=revision_id)
1459
 
            from_branch = None
 
1441
            from_branch=None
1460
1442
        tree = checkout.create_workingtree(revision_id,
1461
1443
                                           from_branch=from_branch,
1462
1444
                                           accelerator_tree=accelerator_tree,
1483
1465
 
1484
1466
    def reference_parent(self, file_id, path, possible_transports=None):
1485
1467
        """Return the parent branch for a tree-reference file_id
1486
 
 
1487
1468
        :param file_id: The file_id of the tree reference
1488
1469
        :param path: The path of the file_id in the tree
1489
1470
        :return: A branch associated with the file_id
1542
1523
        else:
1543
1524
            raise AssertionError("invalid heads: %r" % (heads,))
1544
1525
 
1545
 
    def heads_to_fetch(self):
1546
 
        """Return the heads that must and that should be fetched to copy this
1547
 
        branch into another repo.
1548
 
 
1549
 
        :returns: a 2-tuple of (must_fetch, if_present_fetch).  must_fetch is a
1550
 
            set of heads that must be fetched.  if_present_fetch is a set of
1551
 
            heads that must be fetched if present, but no error is necessary if
1552
 
            they are not present.
1553
 
        """
1554
 
        # For bzr native formats must_fetch is just the tip, and
1555
 
        # if_present_fetch are the tags.
1556
 
        must_fetch = set([self.last_revision()])
1557
 
        if_present_fetch = set()
1558
 
        if self.get_config_stack().get('branch.fetch_tags'):
1559
 
            try:
1560
 
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
1561
 
            except errors.TagsNotSupported:
1562
 
                pass
1563
 
        must_fetch.discard(_mod_revision.NULL_REVISION)
1564
 
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
1565
 
        return must_fetch, if_present_fetch
1566
 
 
1567
 
 
1568
 
class BranchFormat(controldir.ControlComponentFormat):
 
1526
 
 
1527
class BranchFormat(object):
1569
1528
    """An encapsulation of the initialization and open routines for a format.
1570
1529
 
1571
1530
    Formats provide three things:
1572
1531
     * An initialization routine,
1573
 
     * a format description
 
1532
     * a format string,
1574
1533
     * an open routine.
1575
1534
 
1576
1535
    Formats are placed in an dict by their format string for reference
1583
1542
    object will be created every time regardless.
1584
1543
    """
1585
1544
 
 
1545
    _default_format = None
 
1546
    """The default format used for new branches."""
 
1547
 
 
1548
    _formats = {}
 
1549
    """The known formats."""
 
1550
 
 
1551
    can_set_append_revisions_only = True
 
1552
 
1586
1553
    def __eq__(self, other):
1587
1554
        return self.__class__ is other.__class__
1588
1555
 
1589
1556
    def __ne__(self, other):
1590
1557
        return not (self == other)
1591
1558
 
1592
 
    def get_reference(self, controldir, name=None):
1593
 
        """Get the target reference of the branch in controldir.
 
1559
    @classmethod
 
1560
    def find_format(klass, a_bzrdir, name=None):
 
1561
        """Return the format for the branch object in a_bzrdir."""
 
1562
        try:
 
1563
            transport = a_bzrdir.get_branch_transport(None, name=name)
 
1564
            format_string = transport.get_bytes("format")
 
1565
            format = klass._formats[format_string]
 
1566
            if isinstance(format, MetaDirBranchFormatFactory):
 
1567
                return format()
 
1568
            return format
 
1569
        except errors.NoSuchFile:
 
1570
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
 
1571
        except KeyError:
 
1572
            raise errors.UnknownFormatError(format=format_string, kind='branch')
 
1573
 
 
1574
    @classmethod
 
1575
    def get_default_format(klass):
 
1576
        """Return the current default format."""
 
1577
        return klass._default_format
 
1578
 
 
1579
    @classmethod
 
1580
    def get_formats(klass):
 
1581
        """Get all the known formats.
 
1582
 
 
1583
        Warning: This triggers a load of all lazy registered formats: do not
 
1584
        use except when that is desireed.
 
1585
        """
 
1586
        result = []
 
1587
        for fmt in klass._formats.values():
 
1588
            if isinstance(fmt, MetaDirBranchFormatFactory):
 
1589
                fmt = fmt()
 
1590
            result.append(fmt)
 
1591
        return result
 
1592
 
 
1593
    def get_reference(self, a_bzrdir, name=None):
 
1594
        """Get the target reference of the branch in a_bzrdir.
1594
1595
 
1595
1596
        format probing must have been completed before calling
1596
1597
        this method - it is assumed that the format of the branch
1597
 
        in controldir is correct.
 
1598
        in a_bzrdir is correct.
1598
1599
 
1599
 
        :param controldir: The controldir to get the branch data from.
 
1600
        :param a_bzrdir: The bzrdir to get the branch data from.
1600
1601
        :param name: Name of the colocated branch to fetch
1601
1602
        :return: None if the branch is not a reference branch.
1602
1603
        """
1603
1604
        return None
1604
1605
 
1605
1606
    @classmethod
1606
 
    def set_reference(self, controldir, name, to_branch):
1607
 
        """Set the target reference of the branch in controldir.
 
1607
    def set_reference(self, a_bzrdir, name, to_branch):
 
1608
        """Set the target reference of the branch in a_bzrdir.
1608
1609
 
1609
1610
        format probing must have been completed before calling
1610
1611
        this method - it is assumed that the format of the branch
1611
 
        in controldir is correct.
 
1612
        in a_bzrdir is correct.
1612
1613
 
1613
 
        :param controldir: The controldir to set the branch reference for.
 
1614
        :param a_bzrdir: The bzrdir to set the branch reference for.
1614
1615
        :param name: Name of colocated branch to set, None for default
1615
1616
        :param to_branch: branch that the checkout is to reference
1616
1617
        """
1617
1618
        raise NotImplementedError(self.set_reference)
1618
1619
 
 
1620
    def get_format_string(self):
 
1621
        """Return the ASCII format string that identifies this format."""
 
1622
        raise NotImplementedError(self.get_format_string)
 
1623
 
1619
1624
    def get_format_description(self):
1620
1625
        """Return the short format description for this format."""
1621
1626
        raise NotImplementedError(self.get_format_description)
1622
1627
 
1623
 
    def _run_post_branch_init_hooks(self, controldir, name, branch):
 
1628
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
1624
1629
        hooks = Branch.hooks['post_branch_init']
1625
1630
        if not hooks:
1626
1631
            return
1627
 
        params = BranchInitHookParams(self, controldir, name, branch)
 
1632
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
1628
1633
        for hook in hooks:
1629
1634
            hook(params)
1630
1635
 
1631
 
    def initialize(self, controldir, name=None, repository=None,
1632
 
                   append_revisions_only=None):
1633
 
        """Create a branch of this format in controldir.
1634
 
 
 
1636
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
1637
                           lock_type='metadir', set_format=True):
 
1638
        """Initialize a branch in a bzrdir, with specified files
 
1639
 
 
1640
        :param a_bzrdir: The bzrdir to initialize the branch in
 
1641
        :param utf8_files: The files to create as a list of
 
1642
            (filename, content) tuples
 
1643
        :param name: Name of colocated branch to create, if any
 
1644
        :param set_format: If True, set the format with
 
1645
            self.get_format_string.  (BzrBranch4 has its format set
 
1646
            elsewhere)
 
1647
        :return: a branch in this format
 
1648
        """
 
1649
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
1650
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
 
1651
        lock_map = {
 
1652
            'metadir': ('lock', lockdir.LockDir),
 
1653
            'branch4': ('branch-lock', lockable_files.TransportLock),
 
1654
        }
 
1655
        lock_name, lock_class = lock_map[lock_type]
 
1656
        control_files = lockable_files.LockableFiles(branch_transport,
 
1657
            lock_name, lock_class)
 
1658
        control_files.create_lock()
 
1659
        try:
 
1660
            control_files.lock_write()
 
1661
        except errors.LockContention:
 
1662
            if lock_type != 'branch4':
 
1663
                raise
 
1664
            lock_taken = False
 
1665
        else:
 
1666
            lock_taken = True
 
1667
        if set_format:
 
1668
            utf8_files += [('format', self.get_format_string())]
 
1669
        try:
 
1670
            for (filename, content) in utf8_files:
 
1671
                branch_transport.put_bytes(
 
1672
                    filename, content,
 
1673
                    mode=a_bzrdir._get_file_mode())
 
1674
        finally:
 
1675
            if lock_taken:
 
1676
                control_files.unlock()
 
1677
        branch = self.open(a_bzrdir, name, _found=True)
 
1678
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
1679
        return branch
 
1680
 
 
1681
    def initialize(self, a_bzrdir, name=None):
 
1682
        """Create a branch of this format in a_bzrdir.
 
1683
        
1635
1684
        :param name: Name of the colocated branch to create.
1636
1685
        """
1637
1686
        raise NotImplementedError(self.initialize)
1657
1706
        Note that it is normal for branch to be a RemoteBranch when using tags
1658
1707
        on a RemoteBranch.
1659
1708
        """
1660
 
        return _mod_tag.DisabledTags(branch)
 
1709
        return DisabledTags(branch)
1661
1710
 
1662
1711
    def network_name(self):
1663
1712
        """A simple byte string uniquely identifying this format for RPC calls.
1669
1718
        """
1670
1719
        raise NotImplementedError(self.network_name)
1671
1720
 
1672
 
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
1673
 
            found_repository=None, possible_transports=None):
1674
 
        """Return the branch object for controldir.
 
1721
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
1722
        """Return the branch object for a_bzrdir
1675
1723
 
1676
 
        :param controldir: A ControlDir that contains a branch.
 
1724
        :param a_bzrdir: A BzrDir that contains a branch.
1677
1725
        :param name: Name of colocated branch to open
1678
1726
        :param _found: a private parameter, do not use it. It is used to
1679
1727
            indicate if format probing has already be done.
1682
1730
        """
1683
1731
        raise NotImplementedError(self.open)
1684
1732
 
 
1733
    @classmethod
 
1734
    def register_format(klass, format):
 
1735
        """Register a metadir format.
 
1736
        
 
1737
        See MetaDirBranchFormatFactory for the ability to register a format
 
1738
        without loading the code the format needs until it is actually used.
 
1739
        """
 
1740
        klass._formats[format.get_format_string()] = format
 
1741
        # Metadir formats have a network name of their format string, and get
 
1742
        # registered as factories.
 
1743
        if isinstance(format, MetaDirBranchFormatFactory):
 
1744
            network_format_registry.register(format.get_format_string(), format)
 
1745
        else:
 
1746
            network_format_registry.register(format.get_format_string(),
 
1747
                format.__class__)
 
1748
 
 
1749
    @classmethod
 
1750
    def set_default_format(klass, format):
 
1751
        klass._default_format = format
 
1752
 
1685
1753
    def supports_set_append_revisions_only(self):
1686
1754
        """True if this format supports set_append_revisions_only."""
1687
1755
        return False
1690
1758
        """True if this format records a stacked-on branch."""
1691
1759
        return False
1692
1760
 
1693
 
    def supports_leaving_lock(self):
1694
 
        """True if this format supports leaving locks in place."""
1695
 
        return False # by default
1696
 
 
1697
1761
    @classmethod
1698
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1699
1762
    def unregister_format(klass, format):
1700
 
        format_registry.remove(format)
 
1763
        del klass._formats[format.get_format_string()]
1701
1764
 
1702
1765
    def __str__(self):
1703
1766
        return self.get_format_description().rstrip()
1706
1769
        """True if this format supports tags stored in the branch"""
1707
1770
        return False  # by default
1708
1771
 
1709
 
    def tags_are_versioned(self):
1710
 
        """Whether the tag container for this branch versions tags."""
1711
 
        return False
1712
 
 
1713
 
    def supports_tags_referencing_ghosts(self):
1714
 
        """True if tags can reference ghost revisions."""
1715
 
        return True
1716
 
 
1717
1772
 
1718
1773
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1719
1774
    """A factory for a BranchFormat object, permitting simple lazy registration.
1733
1788
        """
1734
1789
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
1735
1790
        self._format_string = format_string
1736
 
 
 
1791
        
1737
1792
    def get_format_string(self):
1738
1793
        """See BranchFormat.get_format_string."""
1739
1794
        return self._format_string
1746
1801
class BranchHooks(Hooks):
1747
1802
    """A dictionary mapping hook name to a list of callables for branch hooks.
1748
1803
 
1749
 
    e.g. ['post_push'] Is the list of items to be called when the
1750
 
    push function is invoked.
 
1804
    e.g. ['set_rh'] Is the list of items to be called when the
 
1805
    set_revision_history function is invoked.
1751
1806
    """
1752
1807
 
1753
1808
    def __init__(self):
1756
1811
        These are all empty initially, because by default nothing should get
1757
1812
        notified.
1758
1813
        """
1759
 
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1760
 
        self.add_hook('open',
 
1814
        Hooks.__init__(self)
 
1815
        self.create_hook(HookPoint('set_rh',
 
1816
            "Invoked whenever the revision history has been set via "
 
1817
            "set_revision_history. The api signature is (branch, "
 
1818
            "revision_history), and the branch will be write-locked. "
 
1819
            "The set_rh hook can be expensive for bzr to trigger, a better "
 
1820
            "hook to use is Branch.post_change_branch_tip.", (0, 15), None))
 
1821
        self.create_hook(HookPoint('open',
1761
1822
            "Called with the Branch object that has been opened after a "
1762
 
            "branch is opened.", (1, 8))
1763
 
        self.add_hook('post_push',
 
1823
            "branch is opened.", (1, 8), None))
 
1824
        self.create_hook(HookPoint('post_push',
1764
1825
            "Called after a push operation completes. post_push is called "
1765
1826
            "with a bzrlib.branch.BranchPushResult object and only runs in the "
1766
 
            "bzr client.", (0, 15))
1767
 
        self.add_hook('post_pull',
 
1827
            "bzr client.", (0, 15), None))
 
1828
        self.create_hook(HookPoint('post_pull',
1768
1829
            "Called after a pull operation completes. post_pull is called "
1769
1830
            "with a bzrlib.branch.PullResult object and only runs in the "
1770
 
            "bzr client.", (0, 15))
1771
 
        self.add_hook('pre_commit',
 
1831
            "bzr client.", (0, 15), None))
 
1832
        self.create_hook(HookPoint('pre_commit',
1772
1833
            "Called after a commit is calculated but before it is "
1773
1834
            "completed. pre_commit is called with (local, master, old_revno, "
1774
1835
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1777
1838
            "basis revision. hooks MUST NOT modify this delta. "
1778
1839
            " future_tree is an in-memory tree obtained from "
1779
1840
            "CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1780
 
            "tree.", (0,91))
1781
 
        self.add_hook('post_commit',
 
1841
            "tree.", (0,91), None))
 
1842
        self.create_hook(HookPoint('post_commit',
1782
1843
            "Called in the bzr client after a commit has completed. "
1783
1844
            "post_commit is called with (local, master, old_revno, old_revid, "
1784
1845
            "new_revno, new_revid). old_revid is NULL_REVISION for the first "
1785
 
            "commit to a branch.", (0, 15))
1786
 
        self.add_hook('post_uncommit',
 
1846
            "commit to a branch.", (0, 15), None))
 
1847
        self.create_hook(HookPoint('post_uncommit',
1787
1848
            "Called in the bzr client after an uncommit completes. "
1788
1849
            "post_uncommit is called with (local, master, old_revno, "
1789
1850
            "old_revid, new_revno, new_revid) where local is the local branch "
1790
1851
            "or None, master is the target branch, and an empty branch "
1791
 
            "receives new_revno of 0, new_revid of None.", (0, 15))
1792
 
        self.add_hook('pre_change_branch_tip',
 
1852
            "receives new_revno of 0, new_revid of None.", (0, 15), None))
 
1853
        self.create_hook(HookPoint('pre_change_branch_tip',
1793
1854
            "Called in bzr client and server before a change to the tip of a "
1794
1855
            "branch is made. pre_change_branch_tip is called with a "
1795
1856
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1796
 
            "commit, uncommit will all trigger this hook.", (1, 6))
1797
 
        self.add_hook('post_change_branch_tip',
 
1857
            "commit, uncommit will all trigger this hook.", (1, 6), None))
 
1858
        self.create_hook(HookPoint('post_change_branch_tip',
1798
1859
            "Called in bzr client and server after a change to the tip of a "
1799
1860
            "branch is made. post_change_branch_tip is called with a "
1800
1861
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1801
 
            "commit, uncommit will all trigger this hook.", (1, 4))
1802
 
        self.add_hook('transform_fallback_location',
 
1862
            "commit, uncommit will all trigger this hook.", (1, 4), None))
 
1863
        self.create_hook(HookPoint('transform_fallback_location',
1803
1864
            "Called when a stacked branch is activating its fallback "
1804
1865
            "locations. transform_fallback_location is called with (branch, "
1805
1866
            "url), and should return a new url. Returning the same url "
1810
1871
            "fallback locations have not been activated. When there are "
1811
1872
            "multiple hooks installed for transform_fallback_location, "
1812
1873
            "all are called with the url returned from the previous hook."
1813
 
            "The order is however undefined.", (1, 9))
1814
 
        self.add_hook('automatic_tag_name',
 
1874
            "The order is however undefined.", (1, 9), None))
 
1875
        self.create_hook(HookPoint('automatic_tag_name',
1815
1876
            "Called to determine an automatic tag name for a revision. "
1816
1877
            "automatic_tag_name is called with (branch, revision_id) and "
1817
1878
            "should return a tag name or None if no tag name could be "
1818
1879
            "determined. The first non-None tag name returned will be used.",
1819
 
            (2, 2))
1820
 
        self.add_hook('post_branch_init',
 
1880
            (2, 2), None))
 
1881
        self.create_hook(HookPoint('post_branch_init',
1821
1882
            "Called after new branch initialization completes. "
1822
1883
            "post_branch_init is called with a "
1823
1884
            "bzrlib.branch.BranchInitHookParams. "
1824
1885
            "Note that init, branch and checkout (both heavyweight and "
1825
 
            "lightweight) will all trigger this hook.", (2, 2))
1826
 
        self.add_hook('post_switch',
 
1886
            "lightweight) will all trigger this hook.", (2, 2), None))
 
1887
        self.create_hook(HookPoint('post_switch',
1827
1888
            "Called after a checkout switches branch. "
1828
1889
            "post_switch is called with a "
1829
 
            "bzrlib.branch.SwitchHookParams.", (2, 2))
 
1890
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
1830
1891
 
1831
1892
 
1832
1893
 
1835
1896
 
1836
1897
 
1837
1898
class ChangeBranchTipParams(object):
1838
 
    """Object holding parameters passed to `*_change_branch_tip` hooks.
 
1899
    """Object holding parameters passed to *_change_branch_tip hooks.
1839
1900
 
1840
1901
    There are 5 fields that hooks may wish to access:
1841
1902
 
1873
1934
 
1874
1935
 
1875
1936
class BranchInitHookParams(object):
1876
 
    """Object holding parameters passed to `*_branch_init` hooks.
 
1937
    """Object holding parameters passed to *_branch_init hooks.
1877
1938
 
1878
1939
    There are 4 fields that hooks may wish to access:
1879
1940
 
1880
1941
    :ivar format: the branch format
1881
 
    :ivar bzrdir: the ControlDir where the branch will be/has been initialized
 
1942
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
1882
1943
    :ivar name: name of colocated branch, if any (or None)
1883
1944
    :ivar branch: the branch created
1884
1945
 
1887
1948
    branch, which refer to the original branch.
1888
1949
    """
1889
1950
 
1890
 
    def __init__(self, format, controldir, name, branch):
 
1951
    def __init__(self, format, a_bzrdir, name, branch):
1891
1952
        """Create a group of BranchInitHook parameters.
1892
1953
 
1893
1954
        :param format: the branch format
1894
 
        :param controldir: the ControlDir where the branch will be/has been
 
1955
        :param a_bzrdir: the BzrDir where the branch will be/has been
1895
1956
            initialized
1896
1957
        :param name: name of colocated branch, if any (or None)
1897
1958
        :param branch: the branch created
1901
1962
        in branch, which refer to the original branch.
1902
1963
        """
1903
1964
        self.format = format
1904
 
        self.bzrdir = controldir
 
1965
        self.bzrdir = a_bzrdir
1905
1966
        self.name = name
1906
1967
        self.branch = branch
1907
1968
 
1913
1974
 
1914
1975
 
1915
1976
class SwitchHookParams(object):
1916
 
    """Object holding parameters passed to `*_switch` hooks.
 
1977
    """Object holding parameters passed to *_switch hooks.
1917
1978
 
1918
1979
    There are 4 fields that hooks may wish to access:
1919
1980
 
1920
 
    :ivar control_dir: ControlDir of the checkout to change
 
1981
    :ivar control_dir: BzrDir of the checkout to change
1921
1982
    :ivar to_branch: branch that the checkout is to reference
1922
1983
    :ivar force: skip the check for local commits in a heavy checkout
1923
1984
    :ivar revision_id: revision ID to switch to (or None)
1926
1987
    def __init__(self, control_dir, to_branch, force, revision_id):
1927
1988
        """Create a group of SwitchHook parameters.
1928
1989
 
1929
 
        :param control_dir: ControlDir of the checkout to change
 
1990
        :param control_dir: BzrDir of the checkout to change
1930
1991
        :param to_branch: branch that the checkout is to reference
1931
1992
        :param force: skip the check for local commits in a heavy checkout
1932
1993
        :param revision_id: revision ID to switch to (or None)
1945
2006
            self.revision_id)
1946
2007
 
1947
2008
 
1948
 
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
1949
 
    """Base class for branch formats that live in meta directories.
 
2009
class BzrBranchFormat4(BranchFormat):
 
2010
    """Bzr branch format 4.
 
2011
 
 
2012
    This format has:
 
2013
     - a revision-history file.
 
2014
     - a branch-lock lock file [ to be shared with the bzrdir ]
1950
2015
    """
1951
2016
 
 
2017
    def get_format_description(self):
 
2018
        """See BranchFormat.get_format_description()."""
 
2019
        return "Branch format 4"
 
2020
 
 
2021
    def initialize(self, a_bzrdir, name=None):
 
2022
        """Create a branch of this format in a_bzrdir."""
 
2023
        utf8_files = [('revision-history', ''),
 
2024
                      ('branch-name', ''),
 
2025
                      ]
 
2026
        return self._initialize_helper(a_bzrdir, utf8_files, name=name,
 
2027
                                       lock_type='branch4', set_format=False)
 
2028
 
1952
2029
    def __init__(self):
1953
 
        BranchFormat.__init__(self)
1954
 
        bzrdir.BzrFormat.__init__(self)
1955
 
 
1956
 
    @classmethod
1957
 
    def find_format(klass, controldir, name=None):
1958
 
        """Return the format for the branch object in controldir."""
1959
 
        try:
1960
 
            transport = controldir.get_branch_transport(None, name=name)
1961
 
        except errors.NoSuchFile:
1962
 
            raise errors.NotBranchError(path=name, bzrdir=controldir)
1963
 
        try:
1964
 
            format_string = transport.get_bytes("format")
1965
 
        except errors.NoSuchFile:
1966
 
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1967
 
        return klass._find_format(format_registry, 'branch', format_string)
 
2030
        super(BzrBranchFormat4, self).__init__()
 
2031
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
 
2032
 
 
2033
    def network_name(self):
 
2034
        """The network name for this format is the control dirs disk label."""
 
2035
        return self._matchingbzrdir.get_format_string()
 
2036
 
 
2037
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
2038
        """See BranchFormat.open()."""
 
2039
        if not _found:
 
2040
            # we are being called directly and must probe.
 
2041
            raise NotImplementedError
 
2042
        return BzrBranch(_format=self,
 
2043
                         _control_files=a_bzrdir._control_files,
 
2044
                         a_bzrdir=a_bzrdir,
 
2045
                         name=name,
 
2046
                         _repository=a_bzrdir.open_repository())
 
2047
 
 
2048
    def __str__(self):
 
2049
        return "Bazaar-NG branch format 4"
 
2050
 
 
2051
 
 
2052
class BranchFormatMetadir(BranchFormat):
 
2053
    """Common logic for meta-dir based branch formats."""
1968
2054
 
1969
2055
    def _branch_class(self):
1970
2056
        """What class to instantiate on open calls."""
1971
2057
        raise NotImplementedError(self._branch_class)
1972
2058
 
1973
 
    def _get_initial_config(self, append_revisions_only=None):
1974
 
        if append_revisions_only:
1975
 
            return "append_revisions_only = True\n"
1976
 
        else:
1977
 
            # Avoid writing anything if append_revisions_only is disabled,
1978
 
            # as that is the default.
1979
 
            return ""
1980
 
 
1981
 
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1982
 
                           repository=None):
1983
 
        """Initialize a branch in a control dir, with specified files
1984
 
 
1985
 
        :param a_bzrdir: The bzrdir to initialize the branch in
1986
 
        :param utf8_files: The files to create as a list of
1987
 
            (filename, content) tuples
1988
 
        :param name: Name of colocated branch to create, if any
1989
 
        :return: a branch in this format
 
2059
    def network_name(self):
 
2060
        """A simple byte string uniquely identifying this format for RPC calls.
 
2061
 
 
2062
        Metadir branch formats use their format string.
1990
2063
        """
1991
 
        if name is None:
1992
 
            name = a_bzrdir._get_selected_branch()
1993
 
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1994
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1995
 
        control_files = lockable_files.LockableFiles(branch_transport,
1996
 
            'lock', lockdir.LockDir)
1997
 
        control_files.create_lock()
1998
 
        control_files.lock_write()
1999
 
        try:
2000
 
            utf8_files += [('format', self.as_string())]
2001
 
            for (filename, content) in utf8_files:
2002
 
                branch_transport.put_bytes(
2003
 
                    filename, content,
2004
 
                    mode=a_bzrdir._get_file_mode())
2005
 
        finally:
2006
 
            control_files.unlock()
2007
 
        branch = self.open(a_bzrdir, name, _found=True,
2008
 
                found_repository=repository)
2009
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2010
 
        return branch
 
2064
        return self.get_format_string()
2011
2065
 
2012
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2013
 
            found_repository=None, possible_transports=None):
 
2066
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2014
2067
        """See BranchFormat.open()."""
2015
 
        if name is None:
2016
 
            name = a_bzrdir._get_selected_branch()
2017
2068
        if not _found:
2018
 
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
 
2069
            format = BranchFormat.find_format(a_bzrdir, name=name)
2019
2070
            if format.__class__ != self.__class__:
2020
2071
                raise AssertionError("wrong format %r found for %r" %
2021
2072
                    (format, self))
2023
2074
        try:
2024
2075
            control_files = lockable_files.LockableFiles(transport, 'lock',
2025
2076
                                                         lockdir.LockDir)
2026
 
            if found_repository is None:
2027
 
                found_repository = a_bzrdir.find_repository()
2028
2077
            return self._branch_class()(_format=self,
2029
2078
                              _control_files=control_files,
2030
2079
                              name=name,
2031
2080
                              a_bzrdir=a_bzrdir,
2032
 
                              _repository=found_repository,
2033
 
                              ignore_fallbacks=ignore_fallbacks,
2034
 
                              possible_transports=possible_transports)
 
2081
                              _repository=a_bzrdir.find_repository(),
 
2082
                              ignore_fallbacks=ignore_fallbacks)
2035
2083
        except errors.NoSuchFile:
2036
2084
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2037
2085
 
2038
 
    @property
2039
 
    def _matchingbzrdir(self):
2040
 
        ret = bzrdir.BzrDirMetaFormat1()
2041
 
        ret.set_branch_format(self)
2042
 
        return ret
 
2086
    def __init__(self):
 
2087
        super(BranchFormatMetadir, self).__init__()
 
2088
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
2089
        self._matchingbzrdir.set_branch_format(self)
2043
2090
 
2044
2091
    def supports_tags(self):
2045
2092
        return True
2046
2093
 
2047
 
    def supports_leaving_lock(self):
2048
 
        return True
2049
 
 
2050
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
2051
 
            basedir=None):
2052
 
        BranchFormat.check_support_status(self,
2053
 
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
2054
 
            basedir=basedir)
2055
 
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
2056
 
            recommend_upgrade=recommend_upgrade, basedir=basedir)
2057
 
 
2058
2094
 
2059
2095
class BzrBranchFormat5(BranchFormatMetadir):
2060
2096
    """Bzr branch format 5.
2072
2108
    def _branch_class(self):
2073
2109
        return BzrBranch5
2074
2110
 
2075
 
    @classmethod
2076
 
    def get_format_string(cls):
 
2111
    def get_format_string(self):
2077
2112
        """See BranchFormat.get_format_string()."""
2078
2113
        return "Bazaar-NG branch format 5\n"
2079
2114
 
2081
2116
        """See BranchFormat.get_format_description()."""
2082
2117
        return "Branch format 5"
2083
2118
 
2084
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2085
 
                   append_revisions_only=None):
 
2119
    def initialize(self, a_bzrdir, name=None):
2086
2120
        """Create a branch of this format in a_bzrdir."""
2087
 
        if append_revisions_only:
2088
 
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2089
2121
        utf8_files = [('revision-history', ''),
2090
2122
                      ('branch-name', ''),
2091
2123
                      ]
2092
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2124
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2093
2125
 
2094
2126
    def supports_tags(self):
2095
2127
        return False
2109
2141
    def _branch_class(self):
2110
2142
        return BzrBranch6
2111
2143
 
2112
 
    @classmethod
2113
 
    def get_format_string(cls):
 
2144
    def get_format_string(self):
2114
2145
        """See BranchFormat.get_format_string()."""
2115
2146
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
2116
2147
 
2118
2149
        """See BranchFormat.get_format_description()."""
2119
2150
        return "Branch format 6"
2120
2151
 
2121
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2122
 
                   append_revisions_only=None):
 
2152
    def initialize(self, a_bzrdir, name=None):
2123
2153
        """Create a branch of this format in a_bzrdir."""
2124
2154
        utf8_files = [('last-revision', '0 null:\n'),
2125
 
                      ('branch.conf',
2126
 
                          self._get_initial_config(append_revisions_only)),
 
2155
                      ('branch.conf', ''),
2127
2156
                      ('tags', ''),
2128
2157
                      ]
2129
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2158
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2130
2159
 
2131
2160
    def make_tags(self, branch):
2132
2161
        """See bzrlib.branch.BranchFormat.make_tags()."""
2133
 
        return _mod_tag.BasicTags(branch)
 
2162
        return BasicTags(branch)
2134
2163
 
2135
2164
    def supports_set_append_revisions_only(self):
2136
2165
        return True
2142
2171
    def _branch_class(self):
2143
2172
        return BzrBranch8
2144
2173
 
2145
 
    @classmethod
2146
 
    def get_format_string(cls):
 
2174
    def get_format_string(self):
2147
2175
        """See BranchFormat.get_format_string()."""
2148
2176
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2149
2177
 
2151
2179
        """See BranchFormat.get_format_description()."""
2152
2180
        return "Branch format 8"
2153
2181
 
2154
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2155
 
                   append_revisions_only=None):
 
2182
    def initialize(self, a_bzrdir, name=None):
2156
2183
        """Create a branch of this format in a_bzrdir."""
2157
2184
        utf8_files = [('last-revision', '0 null:\n'),
2158
 
                      ('branch.conf',
2159
 
                          self._get_initial_config(append_revisions_only)),
 
2185
                      ('branch.conf', ''),
2160
2186
                      ('tags', ''),
2161
2187
                      ('references', '')
2162
2188
                      ]
2163
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2189
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2190
 
 
2191
    def __init__(self):
 
2192
        super(BzrBranchFormat8, self).__init__()
 
2193
        self._matchingbzrdir.repository_format = \
 
2194
            RepositoryFormatKnitPack5RichRoot()
2164
2195
 
2165
2196
    def make_tags(self, branch):
2166
2197
        """See bzrlib.branch.BranchFormat.make_tags()."""
2167
 
        return _mod_tag.BasicTags(branch)
 
2198
        return BasicTags(branch)
2168
2199
 
2169
2200
    def supports_set_append_revisions_only(self):
2170
2201
        return True
2175
2206
    supports_reference_locations = True
2176
2207
 
2177
2208
 
2178
 
class BzrBranchFormat7(BranchFormatMetadir):
 
2209
class BzrBranchFormat7(BzrBranchFormat8):
2179
2210
    """Branch format with last-revision, tags, and a stacked location pointer.
2180
2211
 
2181
2212
    The stacked location pointer is passed down to the repository and requires
2184
2215
    This format was introduced in bzr 1.6.
2185
2216
    """
2186
2217
 
2187
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2188
 
                   append_revisions_only=None):
 
2218
    def initialize(self, a_bzrdir, name=None):
2189
2219
        """Create a branch of this format in a_bzrdir."""
2190
2220
        utf8_files = [('last-revision', '0 null:\n'),
2191
 
                      ('branch.conf',
2192
 
                          self._get_initial_config(append_revisions_only)),
 
2221
                      ('branch.conf', ''),
2193
2222
                      ('tags', ''),
2194
2223
                      ]
2195
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2224
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2196
2225
 
2197
2226
    def _branch_class(self):
2198
2227
        return BzrBranch7
2199
2228
 
2200
 
    @classmethod
2201
 
    def get_format_string(cls):
 
2229
    def get_format_string(self):
2202
2230
        """See BranchFormat.get_format_string()."""
2203
2231
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2204
2232
 
2209
2237
    def supports_set_append_revisions_only(self):
2210
2238
        return True
2211
2239
 
2212
 
    def supports_stacking(self):
2213
 
        return True
2214
 
 
2215
 
    def make_tags(self, branch):
2216
 
        """See bzrlib.branch.BranchFormat.make_tags()."""
2217
 
        return _mod_tag.BasicTags(branch)
2218
 
 
2219
2240
    supports_reference_locations = False
2220
2241
 
2221
2242
 
2222
 
class BranchReferenceFormat(BranchFormatMetadir):
 
2243
class BranchReferenceFormat(BranchFormat):
2223
2244
    """Bzr branch reference format.
2224
2245
 
2225
2246
    Branch references are used in implementing checkouts, they
2230
2251
     - a format string
2231
2252
    """
2232
2253
 
2233
 
    @classmethod
2234
 
    def get_format_string(cls):
 
2254
    def get_format_string(self):
2235
2255
        """See BranchFormat.get_format_string()."""
2236
2256
        return "Bazaar-NG Branch Reference Format 1\n"
2237
2257
 
2249
2269
        transport = a_bzrdir.get_branch_transport(None, name=name)
2250
2270
        location = transport.put_bytes('location', to_branch.base)
2251
2271
 
2252
 
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2253
 
            repository=None, append_revisions_only=None):
 
2272
    def initialize(self, a_bzrdir, name=None, target_branch=None):
2254
2273
        """Create a branch of this format in a_bzrdir."""
2255
2274
        if target_branch is None:
2256
2275
            # this format does not implement branch itself, thus the implicit
2257
2276
            # creation contract must see it as uninitializable
2258
2277
            raise errors.UninitializableFormat(self)
2259
2278
        mutter('creating branch reference in %s', a_bzrdir.user_url)
2260
 
        if a_bzrdir._format.fixed_components:
2261
 
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
2262
 
        if name is None:
2263
 
            name = a_bzrdir._get_selected_branch()
2264
2279
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2265
2280
        branch_transport.put_bytes('location',
2266
 
            target_branch.user_url)
2267
 
        branch_transport.put_bytes('format', self.as_string())
2268
 
        branch = self.open(a_bzrdir, name, _found=True,
 
2281
            target_branch.bzrdir.user_url)
 
2282
        branch_transport.put_bytes('format', self.get_format_string())
 
2283
        branch = self.open(
 
2284
            a_bzrdir, name, _found=True,
2269
2285
            possible_transports=[target_branch.bzrdir.root_transport])
2270
2286
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2271
2287
        return branch
2272
2288
 
 
2289
    def __init__(self):
 
2290
        super(BranchReferenceFormat, self).__init__()
 
2291
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
2292
        self._matchingbzrdir.set_branch_format(self)
 
2293
 
2273
2294
    def _make_reference_clone_function(format, a_branch):
2274
2295
        """Create a clone() routine for a branch dynamically."""
2275
2296
        def clone(to_bzrdir, revision_id=None,
2282
2303
        return clone
2283
2304
 
2284
2305
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2285
 
             possible_transports=None, ignore_fallbacks=False,
2286
 
             found_repository=None):
 
2306
             possible_transports=None, ignore_fallbacks=False):
2287
2307
        """Return the branch that the branch reference in a_bzrdir points at.
2288
2308
 
2289
2309
        :param a_bzrdir: A BzrDir that contains a branch.
2297
2317
            a_bzrdir.
2298
2318
        :param possible_transports: An optional reusable transports list.
2299
2319
        """
2300
 
        if name is None:
2301
 
            name = a_bzrdir._get_selected_branch()
2302
2320
        if not _found:
2303
 
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
 
2321
            format = BranchFormat.find_format(a_bzrdir, name=name)
2304
2322
            if format.__class__ != self.__class__:
2305
2323
                raise AssertionError("wrong format %r found for %r" %
2306
2324
                    (format, self))
2307
2325
        if location is None:
2308
2326
            location = self.get_reference(a_bzrdir, name)
2309
 
        real_bzrdir = controldir.ControlDir.open(
 
2327
        real_bzrdir = bzrdir.BzrDir.open(
2310
2328
            location, possible_transports=possible_transports)
2311
 
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
2312
 
            possible_transports=possible_transports)
 
2329
        result = real_bzrdir.open_branch(name=name, 
 
2330
            ignore_fallbacks=ignore_fallbacks)
2313
2331
        # this changes the behaviour of result.clone to create a new reference
2314
2332
        # rather than a copy of the content of the branch.
2315
2333
        # I did not use a proxy object because that needs much more extensive
2322
2340
        return result
2323
2341
 
2324
2342
 
2325
 
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
2326
 
    """Branch format registry."""
2327
 
 
2328
 
    def __init__(self, other_registry=None):
2329
 
        super(BranchFormatRegistry, self).__init__(other_registry)
2330
 
        self._default_format = None
2331
 
 
2332
 
    def set_default(self, format):
2333
 
        self._default_format = format
2334
 
 
2335
 
    def get_default(self):
2336
 
        return self._default_format
2337
 
 
2338
 
 
2339
2343
network_format_registry = registry.FormatRegistry()
2340
2344
"""Registry of formats indexed by their network name.
2341
2345
 
2344
2348
BranchFormat.network_name() for more detail.
2345
2349
"""
2346
2350
 
2347
 
format_registry = BranchFormatRegistry(network_format_registry)
2348
 
 
2349
2351
 
2350
2352
# formats which have no format string are not discoverable
2351
2353
# and not independently creatable, so are not registered.
2353
2355
__format6 = BzrBranchFormat6()
2354
2356
__format7 = BzrBranchFormat7()
2355
2357
__format8 = BzrBranchFormat8()
2356
 
format_registry.register(__format5)
2357
 
format_registry.register(BranchReferenceFormat())
2358
 
format_registry.register(__format6)
2359
 
format_registry.register(__format7)
2360
 
format_registry.register(__format8)
2361
 
format_registry.set_default(__format7)
 
2358
BranchFormat.register_format(__format5)
 
2359
BranchFormat.register_format(BranchReferenceFormat())
 
2360
BranchFormat.register_format(__format6)
 
2361
BranchFormat.register_format(__format7)
 
2362
BranchFormat.register_format(__format8)
 
2363
BranchFormat.set_default_format(__format7)
 
2364
_legacy_formats = [BzrBranchFormat4(),
 
2365
    ]
 
2366
network_format_registry.register(
 
2367
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2362
2368
 
2363
2369
 
2364
2370
class BranchWriteLockResult(LogicalLockResult):
2396
2402
 
2397
2403
    def __init__(self, _format=None,
2398
2404
                 _control_files=None, a_bzrdir=None, name=None,
2399
 
                 _repository=None, ignore_fallbacks=False,
2400
 
                 possible_transports=None):
 
2405
                 _repository=None, ignore_fallbacks=False):
2401
2406
        """Create new branch object at a particular location."""
2402
2407
        if a_bzrdir is None:
2403
2408
            raise ValueError('a_bzrdir must be supplied')
2404
 
        if name is None:
2405
 
            raise ValueError('name must be supplied')
2406
 
        self.bzrdir = a_bzrdir
2407
 
        self._user_transport = self.bzrdir.transport.clone('..')
2408
 
        if name != "":
2409
 
            self._user_transport.set_segment_parameter(
2410
 
                "branch", urlutils.escape(name))
2411
 
        self._base = self._user_transport.base
 
2409
        else:
 
2410
            self.bzrdir = a_bzrdir
 
2411
        self._base = self.bzrdir.transport.clone('..').base
2412
2412
        self.name = name
 
2413
        # XXX: We should be able to just do
 
2414
        #   self.base = self.bzrdir.root_transport.base
 
2415
        # but this does not quite work yet -- mbp 20080522
2413
2416
        self._format = _format
2414
2417
        if _control_files is None:
2415
2418
            raise ValueError('BzrBranch _control_files is None')
2416
2419
        self.control_files = _control_files
2417
2420
        self._transport = _control_files._transport
2418
2421
        self.repository = _repository
2419
 
        self.conf_store = None
2420
 
        Branch.__init__(self, possible_transports)
 
2422
        Branch.__init__(self)
2421
2423
 
2422
2424
    def __str__(self):
2423
 
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
 
2425
        if self.name is None:
 
2426
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
 
2427
        else:
 
2428
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
 
2429
                self.name)
2424
2430
 
2425
2431
    __repr__ = __str__
2426
2432
 
2430
2436
 
2431
2437
    base = property(_get_base, doc="The URL for the root of this branch.")
2432
2438
 
2433
 
    @property
2434
 
    def user_transport(self):
2435
 
        return self._user_transport
2436
 
 
2437
2439
    def _get_config(self):
2438
 
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
2439
 
 
2440
 
    def _get_config_store(self):
2441
 
        if self.conf_store is None:
2442
 
            self.conf_store =  _mod_config.BranchStore(self)
2443
 
        return self.conf_store
 
2440
        return TransportConfig(self._transport, 'branch.conf')
2444
2441
 
2445
2442
    def is_locked(self):
2446
2443
        return self.control_files.is_locked()
2495
2492
 
2496
2493
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2497
2494
    def unlock(self):
2498
 
        if self.control_files._lock_count == 1 and self.conf_store is not None:
2499
 
            self.conf_store.save_changes()
2500
2495
        try:
2501
2496
            self.control_files.unlock()
2502
2497
        finally:
2523
2518
        """See Branch.print_file."""
2524
2519
        return self.repository.print_file(file, revision_id)
2525
2520
 
 
2521
    def _write_revision_history(self, history):
 
2522
        """Factored out of set_revision_history.
 
2523
 
 
2524
        This performs the actual writing to disk.
 
2525
        It is intended to be called by BzrBranch5.set_revision_history."""
 
2526
        self._transport.put_bytes(
 
2527
            'revision-history', '\n'.join(history),
 
2528
            mode=self.bzrdir._get_file_mode())
 
2529
 
 
2530
    @needs_write_lock
 
2531
    def set_revision_history(self, rev_history):
 
2532
        """See Branch.set_revision_history."""
 
2533
        if 'evil' in debug.debug_flags:
 
2534
            mutter_callsite(3, "set_revision_history scales with history.")
 
2535
        check_not_reserved_id = _mod_revision.check_not_reserved_id
 
2536
        for rev_id in rev_history:
 
2537
            check_not_reserved_id(rev_id)
 
2538
        if Branch.hooks['post_change_branch_tip']:
 
2539
            # Don't calculate the last_revision_info() if there are no hooks
 
2540
            # that will use it.
 
2541
            old_revno, old_revid = self.last_revision_info()
 
2542
        if len(rev_history) == 0:
 
2543
            revid = _mod_revision.NULL_REVISION
 
2544
        else:
 
2545
            revid = rev_history[-1]
 
2546
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
 
2547
        self._write_revision_history(rev_history)
 
2548
        self._clear_cached_state()
 
2549
        self._cache_revision_history(rev_history)
 
2550
        for hook in Branch.hooks['set_rh']:
 
2551
            hook(self, rev_history)
 
2552
        if Branch.hooks['post_change_branch_tip']:
 
2553
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2554
 
 
2555
    def _synchronize_history(self, destination, revision_id):
 
2556
        """Synchronize last revision and revision history between branches.
 
2557
 
 
2558
        This version is most efficient when the destination is also a
 
2559
        BzrBranch5, but works for BzrBranch6 as long as the revision
 
2560
        history is the true lefthand parent history, and all of the revisions
 
2561
        are in the destination's repository.  If not, set_revision_history
 
2562
        will fail.
 
2563
 
 
2564
        :param destination: The branch to copy the history into
 
2565
        :param revision_id: The revision-id to truncate history at.  May
 
2566
          be None to copy complete history.
 
2567
        """
 
2568
        if not isinstance(destination._format, BzrBranchFormat5):
 
2569
            super(BzrBranch, self)._synchronize_history(
 
2570
                destination, revision_id)
 
2571
            return
 
2572
        if revision_id == _mod_revision.NULL_REVISION:
 
2573
            new_history = []
 
2574
        else:
 
2575
            new_history = self.revision_history()
 
2576
        if revision_id is not None and new_history != []:
 
2577
            try:
 
2578
                new_history = new_history[:new_history.index(revision_id) + 1]
 
2579
            except ValueError:
 
2580
                rev = self.repository.get_revision(revision_id)
 
2581
                new_history = rev.get_history(self.repository)[1:]
 
2582
        destination.set_revision_history(new_history)
 
2583
 
2526
2584
    @needs_write_lock
2527
2585
    def set_last_revision_info(self, revno, revision_id):
2528
 
        if not revision_id or not isinstance(revision_id, basestring):
2529
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2586
        """Set the last revision of this branch.
 
2587
 
 
2588
        The caller is responsible for checking that the revno is correct
 
2589
        for this revision id.
 
2590
 
 
2591
        It may be possible to set the branch last revision to an id not
 
2592
        present in the repository.  However, branches can also be
 
2593
        configured to check constraints on history, in which case this may not
 
2594
        be permitted.
 
2595
        """
2530
2596
        revision_id = _mod_revision.ensure_null(revision_id)
2531
 
        old_revno, old_revid = self.last_revision_info()
2532
 
        if self.get_append_revisions_only():
2533
 
            self._check_history_violation(revision_id)
2534
 
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2535
 
        self._write_last_revision_info(revno, revision_id)
2536
 
        self._clear_cached_state()
2537
 
        self._last_revision_info_cache = revno, revision_id
2538
 
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2597
        # this old format stores the full history, but this api doesn't
 
2598
        # provide it, so we must generate, and might as well check it's
 
2599
        # correct
 
2600
        history = self._lefthand_history(revision_id)
 
2601
        if len(history) != revno:
 
2602
            raise AssertionError('%d != %d' % (len(history), revno))
 
2603
        self.set_revision_history(history)
 
2604
 
 
2605
    def _gen_revision_history(self):
 
2606
        history = self._transport.get_bytes('revision-history').split('\n')
 
2607
        if history[-1:] == ['']:
 
2608
            # There shouldn't be a trailing newline, but just in case.
 
2609
            history.pop()
 
2610
        return history
 
2611
 
 
2612
    @needs_write_lock
 
2613
    def generate_revision_history(self, revision_id, last_rev=None,
 
2614
        other_branch=None):
 
2615
        """Create a new revision history that will finish with revision_id.
 
2616
 
 
2617
        :param revision_id: the new tip to use.
 
2618
        :param last_rev: The previous last_revision. If not None, then this
 
2619
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
2620
        :param other_branch: The other branch that DivergedBranches should
 
2621
            raise with respect to.
 
2622
        """
 
2623
        self.set_revision_history(self._lefthand_history(revision_id,
 
2624
            last_rev, other_branch))
2539
2625
 
2540
2626
    def basis_tree(self):
2541
2627
        """See Branch.basis_tree."""
2550
2636
                pass
2551
2637
        return None
2552
2638
 
 
2639
    def _basic_push(self, target, overwrite, stop_revision):
 
2640
        """Basic implementation of push without bound branches or hooks.
 
2641
 
 
2642
        Must be called with source read locked and target write locked.
 
2643
        """
 
2644
        result = BranchPushResult()
 
2645
        result.source_branch = self
 
2646
        result.target_branch = target
 
2647
        result.old_revno, result.old_revid = target.last_revision_info()
 
2648
        self.update_references(target)
 
2649
        if result.old_revid != self.last_revision():
 
2650
            # We assume that during 'push' this repository is closer than
 
2651
            # the target.
 
2652
            graph = self.repository.get_graph(target.repository)
 
2653
            target.update_revisions(self, stop_revision,
 
2654
                overwrite=overwrite, graph=graph)
 
2655
        if self._push_should_merge_tags():
 
2656
            result.tag_conflicts = self.tags.merge_to(target.tags,
 
2657
                overwrite)
 
2658
        result.new_revno, result.new_revid = target.last_revision_info()
 
2659
        return result
 
2660
 
2553
2661
    def get_stacked_on_url(self):
2554
2662
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2555
2663
 
2566
2674
            self._transport.put_bytes('parent', url + '\n',
2567
2675
                mode=self.bzrdir._get_file_mode())
2568
2676
 
 
2677
 
 
2678
class BzrBranch5(BzrBranch):
 
2679
    """A format 5 branch. This supports new features over plain branches.
 
2680
 
 
2681
    It has support for a master_branch which is the data for bound branches.
 
2682
    """
 
2683
 
 
2684
    def get_bound_location(self):
 
2685
        try:
 
2686
            return self._transport.get_bytes('bound')[:-1]
 
2687
        except errors.NoSuchFile:
 
2688
            return None
 
2689
 
 
2690
    @needs_read_lock
 
2691
    def get_master_branch(self, possible_transports=None):
 
2692
        """Return the branch we are bound to.
 
2693
 
 
2694
        :return: Either a Branch, or None
 
2695
 
 
2696
        This could memoise the branch, but if thats done
 
2697
        it must be revalidated on each new lock.
 
2698
        So for now we just don't memoise it.
 
2699
        # RBC 20060304 review this decision.
 
2700
        """
 
2701
        bound_loc = self.get_bound_location()
 
2702
        if not bound_loc:
 
2703
            return None
 
2704
        try:
 
2705
            return Branch.open(bound_loc,
 
2706
                               possible_transports=possible_transports)
 
2707
        except (errors.NotBranchError, errors.ConnectionError), e:
 
2708
            raise errors.BoundBranchConnectionFailure(
 
2709
                    self, bound_loc, e)
 
2710
 
2569
2711
    @needs_write_lock
2570
 
    def unbind(self):
2571
 
        """If bound, unbind"""
2572
 
        return self.set_bound_location(None)
 
2712
    def set_bound_location(self, location):
 
2713
        """Set the target where this branch is bound to.
 
2714
 
 
2715
        :param location: URL to the target branch
 
2716
        """
 
2717
        if location:
 
2718
            self._transport.put_bytes('bound', location+'\n',
 
2719
                mode=self.bzrdir._get_file_mode())
 
2720
        else:
 
2721
            try:
 
2722
                self._transport.delete('bound')
 
2723
            except errors.NoSuchFile:
 
2724
                return False
 
2725
            return True
2573
2726
 
2574
2727
    @needs_write_lock
2575
2728
    def bind(self, other):
2597
2750
        # history around
2598
2751
        self.set_bound_location(other.base)
2599
2752
 
2600
 
    def get_bound_location(self):
2601
 
        try:
2602
 
            return self._transport.get_bytes('bound')[:-1]
2603
 
        except errors.NoSuchFile:
2604
 
            return None
2605
 
 
2606
 
    @needs_read_lock
2607
 
    def get_master_branch(self, possible_transports=None):
2608
 
        """Return the branch we are bound to.
2609
 
 
2610
 
        :return: Either a Branch, or None
2611
 
        """
2612
 
        if self._master_branch_cache is None:
2613
 
            self._master_branch_cache = self._get_master_branch(
2614
 
                possible_transports)
2615
 
        return self._master_branch_cache
2616
 
 
2617
 
    def _get_master_branch(self, possible_transports):
2618
 
        bound_loc = self.get_bound_location()
2619
 
        if not bound_loc:
2620
 
            return None
2621
 
        try:
2622
 
            return Branch.open(bound_loc,
2623
 
                               possible_transports=possible_transports)
2624
 
        except (errors.NotBranchError, errors.ConnectionError), e:
2625
 
            raise errors.BoundBranchConnectionFailure(
2626
 
                    self, bound_loc, e)
2627
 
 
2628
2753
    @needs_write_lock
2629
 
    def set_bound_location(self, location):
2630
 
        """Set the target where this branch is bound to.
2631
 
 
2632
 
        :param location: URL to the target branch
2633
 
        """
2634
 
        self._master_branch_cache = None
2635
 
        if location:
2636
 
            self._transport.put_bytes('bound', location+'\n',
2637
 
                mode=self.bzrdir._get_file_mode())
2638
 
        else:
2639
 
            try:
2640
 
                self._transport.delete('bound')
2641
 
            except errors.NoSuchFile:
2642
 
                return False
2643
 
            return True
 
2754
    def unbind(self):
 
2755
        """If bound, unbind"""
 
2756
        return self.set_bound_location(None)
2644
2757
 
2645
2758
    @needs_write_lock
2646
2759
    def update(self, possible_transports=None):
2659
2772
            return old_tip
2660
2773
        return None
2661
2774
 
2662
 
    def _read_last_revision_info(self):
2663
 
        revision_string = self._transport.get_bytes('last-revision')
2664
 
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2665
 
        revision_id = cache_utf8.get_cached_utf8(revision_id)
2666
 
        revno = int(revno)
2667
 
        return revno, revision_id
2668
 
 
2669
 
    def _write_last_revision_info(self, revno, revision_id):
2670
 
        """Simply write out the revision id, with no checks.
2671
 
 
2672
 
        Use set_last_revision_info to perform this safely.
2673
 
 
2674
 
        Does not update the revision_history cache.
2675
 
        """
2676
 
        revision_id = _mod_revision.ensure_null(revision_id)
2677
 
        out_string = '%d %s\n' % (revno, revision_id)
2678
 
        self._transport.put_bytes('last-revision', out_string,
2679
 
            mode=self.bzrdir._get_file_mode())
2680
 
 
2681
 
    @needs_write_lock
2682
 
    def update_feature_flags(self, updated_flags):
2683
 
        """Update the feature flags for this branch.
2684
 
 
2685
 
        :param updated_flags: Dictionary mapping feature names to necessities
2686
 
            A necessity can be None to indicate the feature should be removed
2687
 
        """
2688
 
        self._format._update_feature_flags(updated_flags)
2689
 
        self.control_transport.put_bytes('format', self._format.as_string())
2690
 
 
2691
 
 
2692
 
class FullHistoryBzrBranch(BzrBranch):
2693
 
    """Bzr branch which contains the full revision history."""
2694
 
 
2695
 
    @needs_write_lock
2696
 
    def set_last_revision_info(self, revno, revision_id):
2697
 
        if not revision_id or not isinstance(revision_id, basestring):
2698
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2699
 
        revision_id = _mod_revision.ensure_null(revision_id)
2700
 
        # this old format stores the full history, but this api doesn't
2701
 
        # provide it, so we must generate, and might as well check it's
2702
 
        # correct
2703
 
        history = self._lefthand_history(revision_id)
2704
 
        if len(history) != revno:
2705
 
            raise AssertionError('%d != %d' % (len(history), revno))
2706
 
        self._set_revision_history(history)
2707
 
 
2708
 
    def _read_last_revision_info(self):
2709
 
        rh = self._revision_history()
2710
 
        revno = len(rh)
2711
 
        if revno:
2712
 
            return (revno, rh[-1])
2713
 
        else:
2714
 
            return (0, _mod_revision.NULL_REVISION)
2715
 
 
2716
 
    def _set_revision_history(self, rev_history):
2717
 
        if 'evil' in debug.debug_flags:
2718
 
            mutter_callsite(3, "set_revision_history scales with history.")
2719
 
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2720
 
        for rev_id in rev_history:
2721
 
            check_not_reserved_id(rev_id)
2722
 
        if Branch.hooks['post_change_branch_tip']:
2723
 
            # Don't calculate the last_revision_info() if there are no hooks
2724
 
            # that will use it.
2725
 
            old_revno, old_revid = self.last_revision_info()
2726
 
        if len(rev_history) == 0:
2727
 
            revid = _mod_revision.NULL_REVISION
2728
 
        else:
2729
 
            revid = rev_history[-1]
2730
 
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2731
 
        self._write_revision_history(rev_history)
2732
 
        self._clear_cached_state()
2733
 
        self._cache_revision_history(rev_history)
2734
 
        if Branch.hooks['post_change_branch_tip']:
2735
 
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2736
 
 
2737
 
    def _write_revision_history(self, history):
2738
 
        """Factored out of set_revision_history.
2739
 
 
2740
 
        This performs the actual writing to disk.
2741
 
        It is intended to be called by set_revision_history."""
2742
 
        self._transport.put_bytes(
2743
 
            'revision-history', '\n'.join(history),
2744
 
            mode=self.bzrdir._get_file_mode())
2745
 
 
2746
 
    def _gen_revision_history(self):
2747
 
        history = self._transport.get_bytes('revision-history').split('\n')
2748
 
        if history[-1:] == ['']:
2749
 
            # There shouldn't be a trailing newline, but just in case.
2750
 
            history.pop()
2751
 
        return history
2752
 
 
2753
 
    def _synchronize_history(self, destination, revision_id):
2754
 
        if not isinstance(destination, FullHistoryBzrBranch):
2755
 
            super(BzrBranch, self)._synchronize_history(
2756
 
                destination, revision_id)
2757
 
            return
2758
 
        if revision_id == _mod_revision.NULL_REVISION:
2759
 
            new_history = []
2760
 
        else:
2761
 
            new_history = self._revision_history()
2762
 
        if revision_id is not None and new_history != []:
2763
 
            try:
2764
 
                new_history = new_history[:new_history.index(revision_id) + 1]
2765
 
            except ValueError:
2766
 
                rev = self.repository.get_revision(revision_id)
2767
 
                new_history = rev.get_history(self.repository)[1:]
2768
 
        destination._set_revision_history(new_history)
2769
 
 
2770
 
    @needs_write_lock
2771
 
    def generate_revision_history(self, revision_id, last_rev=None,
2772
 
        other_branch=None):
2773
 
        """Create a new revision history that will finish with revision_id.
2774
 
 
2775
 
        :param revision_id: the new tip to use.
2776
 
        :param last_rev: The previous last_revision. If not None, then this
2777
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
2778
 
        :param other_branch: The other branch that DivergedBranches should
2779
 
            raise with respect to.
2780
 
        """
2781
 
        self._set_revision_history(self._lefthand_history(revision_id,
2782
 
            last_rev, other_branch))
2783
 
 
2784
 
 
2785
 
class BzrBranch5(FullHistoryBzrBranch):
2786
 
    """A format 5 branch. This supports new features over plain branches.
2787
 
 
2788
 
    It has support for a master_branch which is the data for bound branches.
2789
 
    """
2790
 
 
2791
 
 
2792
 
class BzrBranch8(BzrBranch):
 
2775
 
 
2776
class BzrBranch8(BzrBranch5):
2793
2777
    """A branch that stores tree-reference locations."""
2794
2778
 
2795
 
    def _open_hook(self, possible_transports=None):
 
2779
    def _open_hook(self):
2796
2780
        if self._ignore_fallbacks:
2797
2781
            return
2798
 
        if possible_transports is None:
2799
 
            possible_transports = [self.bzrdir.root_transport]
2800
2782
        try:
2801
2783
            url = self.get_stacked_on_url()
2802
2784
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2810
2792
                    raise AssertionError(
2811
2793
                        "'transform_fallback_location' hook %s returned "
2812
2794
                        "None, not a URL." % hook_name)
2813
 
            self._activate_fallback_location(url,
2814
 
                possible_transports=possible_transports)
 
2795
            self._activate_fallback_location(url)
2815
2796
 
2816
2797
    def __init__(self, *args, **kwargs):
2817
2798
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2824
2805
        self._last_revision_info_cache = None
2825
2806
        self._reference_info = None
2826
2807
 
 
2808
    def _last_revision_info(self):
 
2809
        revision_string = self._transport.get_bytes('last-revision')
 
2810
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
 
2811
        revision_id = cache_utf8.get_cached_utf8(revision_id)
 
2812
        revno = int(revno)
 
2813
        return revno, revision_id
 
2814
 
 
2815
    def _write_last_revision_info(self, revno, revision_id):
 
2816
        """Simply write out the revision id, with no checks.
 
2817
 
 
2818
        Use set_last_revision_info to perform this safely.
 
2819
 
 
2820
        Does not update the revision_history cache.
 
2821
        Intended to be called by set_last_revision_info and
 
2822
        _write_revision_history.
 
2823
        """
 
2824
        revision_id = _mod_revision.ensure_null(revision_id)
 
2825
        out_string = '%d %s\n' % (revno, revision_id)
 
2826
        self._transport.put_bytes('last-revision', out_string,
 
2827
            mode=self.bzrdir._get_file_mode())
 
2828
 
 
2829
    @needs_write_lock
 
2830
    def set_last_revision_info(self, revno, revision_id):
 
2831
        revision_id = _mod_revision.ensure_null(revision_id)
 
2832
        old_revno, old_revid = self.last_revision_info()
 
2833
        if self._get_append_revisions_only():
 
2834
            self._check_history_violation(revision_id)
 
2835
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
 
2836
        self._write_last_revision_info(revno, revision_id)
 
2837
        self._clear_cached_state()
 
2838
        self._last_revision_info_cache = revno, revision_id
 
2839
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2840
 
 
2841
    def _synchronize_history(self, destination, revision_id):
 
2842
        """Synchronize last revision and revision history between branches.
 
2843
 
 
2844
        :see: Branch._synchronize_history
 
2845
        """
 
2846
        # XXX: The base Branch has a fast implementation of this method based
 
2847
        # on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
 
2848
        # that uses set_revision_history.  This class inherits from BzrBranch5,
 
2849
        # but wants the fast implementation, so it calls
 
2850
        # Branch._synchronize_history directly.
 
2851
        Branch._synchronize_history(self, destination, revision_id)
 
2852
 
2827
2853
    def _check_history_violation(self, revision_id):
2828
 
        current_revid = self.last_revision()
2829
 
        last_revision = _mod_revision.ensure_null(current_revid)
 
2854
        last_revision = _mod_revision.ensure_null(self.last_revision())
2830
2855
        if _mod_revision.is_null(last_revision):
2831
2856
            return
2832
 
        graph = self.repository.get_graph()
2833
 
        for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2834
 
            if lh_ancestor == current_revid:
2835
 
                return
2836
 
        raise errors.AppendRevisionsOnlyViolation(self.user_url)
 
2857
        if last_revision not in self._lefthand_history(revision_id):
 
2858
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
2837
2859
 
2838
2860
    def _gen_revision_history(self):
2839
2861
        """Generate the revision history from last revision
2842
2864
        self._extend_partial_history(stop_index=last_revno-1)
2843
2865
        return list(reversed(self._partial_revision_history_cache))
2844
2866
 
 
2867
    def _write_revision_history(self, history):
 
2868
        """Factored out of set_revision_history.
 
2869
 
 
2870
        This performs the actual writing to disk, with format-specific checks.
 
2871
        It is intended to be called by BzrBranch5.set_revision_history.
 
2872
        """
 
2873
        if len(history) == 0:
 
2874
            last_revision = 'null:'
 
2875
        else:
 
2876
            if history != self._lefthand_history(history[-1]):
 
2877
                raise errors.NotLefthandHistory(history)
 
2878
            last_revision = history[-1]
 
2879
        if self._get_append_revisions_only():
 
2880
            self._check_history_violation(last_revision)
 
2881
        self._write_last_revision_info(len(history), last_revision)
 
2882
 
2845
2883
    @needs_write_lock
2846
2884
    def _set_parent_location(self, url):
2847
2885
        """Set the parent branch"""
2933
2971
 
2934
2972
    def set_bound_location(self, location):
2935
2973
        """See Branch.set_push_location."""
2936
 
        self._master_branch_cache = None
2937
2974
        result = None
2938
 
        conf = self.get_config_stack()
 
2975
        config = self.get_config()
2939
2976
        if location is None:
2940
 
            if not conf.get('bound'):
 
2977
            if config.get_user_option('bound') != 'True':
2941
2978
                return False
2942
2979
            else:
2943
 
                conf.set('bound', 'False')
 
2980
                config.set_user_option('bound', 'False', warn_masked=True)
2944
2981
                return True
2945
2982
        else:
2946
2983
            self._set_config_location('bound_location', location,
2947
 
                                      config=conf)
2948
 
            conf.set('bound', 'True')
 
2984
                                      config=config)
 
2985
            config.set_user_option('bound', 'True', warn_masked=True)
2949
2986
        return True
2950
2987
 
2951
2988
    def _get_bound_location(self, bound):
2952
2989
        """Return the bound location in the config file.
2953
2990
 
2954
2991
        Return None if the bound parameter does not match"""
2955
 
        conf = self.get_config_stack()
2956
 
        if conf.get('bound') != bound:
 
2992
        config = self.get_config()
 
2993
        config_bound = (config.get_user_option('bound') == 'True')
 
2994
        if config_bound != bound:
2957
2995
            return None
2958
 
        return self._get_config_location('bound_location', config=conf)
 
2996
        return self._get_config_location('bound_location', config=config)
2959
2997
 
2960
2998
    def get_bound_location(self):
2961
 
        """See Branch.get_bound_location."""
 
2999
        """See Branch.set_push_location."""
2962
3000
        return self._get_bound_location(True)
2963
3001
 
2964
3002
    def get_old_bound_location(self):
2969
3007
        # you can always ask for the URL; but you might not be able to use it
2970
3008
        # if the repo can't support stacking.
2971
3009
        ## self._check_stackable_repo()
2972
 
        # stacked_on_location is only ever defined in branch.conf, so don't
2973
 
        # waste effort reading the whole stack of config files.
2974
 
        conf = _mod_config.BranchOnlyStack(self)
2975
 
        stacked_url = self._get_config_location('stacked_on_location',
2976
 
                                                config=conf)
 
3010
        stacked_url = self._get_config_location('stacked_on_location')
2977
3011
        if stacked_url is None:
2978
3012
            raise errors.NotStacked(self)
2979
 
        return stacked_url.encode('utf-8')
 
3013
        return stacked_url
 
3014
 
 
3015
    def _get_append_revisions_only(self):
 
3016
        return self.get_config(
 
3017
            ).get_user_option_as_bool('append_revisions_only')
 
3018
 
 
3019
    @needs_write_lock
 
3020
    def generate_revision_history(self, revision_id, last_rev=None,
 
3021
                                  other_branch=None):
 
3022
        """See BzrBranch5.generate_revision_history"""
 
3023
        history = self._lefthand_history(revision_id, last_rev, other_branch)
 
3024
        revno = len(history)
 
3025
        self.set_last_revision_info(revno, revision_id)
2980
3026
 
2981
3027
    @needs_read_lock
2982
3028
    def get_rev_id(self, revno, history=None):
3007
3053
        try:
3008
3054
            index = self._partial_revision_history_cache.index(revision_id)
3009
3055
        except ValueError:
3010
 
            try:
3011
 
                self._extend_partial_history(stop_revision=revision_id)
3012
 
            except errors.RevisionNotPresent, e:
3013
 
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
 
3056
            self._extend_partial_history(stop_revision=revision_id)
3014
3057
            index = len(self._partial_revision_history_cache) - 1
3015
 
            if index < 0:
3016
 
                raise errors.NoSuchRevision(self, revision_id)
3017
3058
            if self._partial_revision_history_cache[index] != revision_id:
3018
3059
                raise errors.NoSuchRevision(self, revision_id)
3019
3060
        return self.revno() - index
3071
3112
    :ivar local_branch: target branch if there is a Master, else None
3072
3113
    :ivar target_branch: Target/destination branch object. (write locked)
3073
3114
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
3074
 
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3075
3115
    """
3076
3116
 
 
3117
    @deprecated_method(deprecated_in((2, 3, 0)))
 
3118
    def __int__(self):
 
3119
        """Return the relative change in revno.
 
3120
 
 
3121
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3122
        """
 
3123
        return self.new_revno - self.old_revno
 
3124
 
3077
3125
    def report(self, to_file):
3078
 
        tag_conflicts = getattr(self, "tag_conflicts", None)
3079
 
        tag_updates = getattr(self, "tag_updates", None)
3080
3126
        if not is_quiet():
3081
 
            if self.old_revid != self.new_revid:
 
3127
            if self.old_revid == self.new_revid:
 
3128
                to_file.write('No revisions to pull.\n')
 
3129
            else:
3082
3130
                to_file.write('Now on revision %d.\n' % self.new_revno)
3083
 
            if tag_updates:
3084
 
                to_file.write('%d tag(s) updated.\n' % len(tag_updates))
3085
 
            if self.old_revid == self.new_revid and not tag_updates:
3086
 
                if not tag_conflicts:
3087
 
                    to_file.write('No revisions or tags to pull.\n')
3088
 
                else:
3089
 
                    to_file.write('No revisions to pull.\n')
3090
3131
        self._show_tag_conficts(to_file)
3091
3132
 
3092
3133
 
3109
3150
        target, otherwise it will be None.
3110
3151
    """
3111
3152
 
 
3153
    @deprecated_method(deprecated_in((2, 3, 0)))
 
3154
    def __int__(self):
 
3155
        """Return the relative change in revno.
 
3156
 
 
3157
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3158
        """
 
3159
        return self.new_revno - self.old_revno
 
3160
 
3112
3161
    def report(self, to_file):
3113
 
        # TODO: This function gets passed a to_file, but then
3114
 
        # ignores it and calls note() instead. This is also
3115
 
        # inconsistent with PullResult(), which writes to stdout.
3116
 
        # -- JRV20110901, bug #838853
3117
 
        tag_conflicts = getattr(self, "tag_conflicts", None)
3118
 
        tag_updates = getattr(self, "tag_updates", None)
3119
 
        if not is_quiet():
3120
 
            if self.old_revid != self.new_revid:
3121
 
                note(gettext('Pushed up to revision %d.') % self.new_revno)
3122
 
            if tag_updates:
3123
 
                note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
3124
 
            if self.old_revid == self.new_revid and not tag_updates:
3125
 
                if not tag_conflicts:
3126
 
                    note(gettext('No new revisions or tags to push.'))
3127
 
                else:
3128
 
                    note(gettext('No new revisions to push.'))
 
3162
        """Write a human-readable description of the result."""
 
3163
        if self.old_revid == self.new_revid:
 
3164
            note('No new revisions to push.')
 
3165
        else:
 
3166
            note('Pushed up to revision %d.' % self.new_revno)
3129
3167
        self._show_tag_conficts(to_file)
3130
3168
 
3131
3169
 
3145
3183
        :param verbose: Requests more detailed display of what was checked,
3146
3184
            if any.
3147
3185
        """
3148
 
        note(gettext('checked branch {0} format {1}').format(
3149
 
                                self.branch.user_url, self.branch._format))
 
3186
        note('checked branch %s format %s', self.branch.user_url,
 
3187
            self.branch._format)
3150
3188
        for error in self.errors:
3151
 
            note(gettext('found error:%s'), error)
 
3189
            note('found error:%s', error)
3152
3190
 
3153
3191
 
3154
3192
class Converter5to6(object):
3161
3199
 
3162
3200
        # Copy source data into target
3163
3201
        new_branch._write_last_revision_info(*branch.last_revision_info())
3164
 
        new_branch.lock_write()
3165
 
        try:
3166
 
            new_branch.set_parent(branch.get_parent())
3167
 
            new_branch.set_bound_location(branch.get_bound_location())
3168
 
            new_branch.set_push_location(branch.get_push_location())
3169
 
        finally:
3170
 
            new_branch.unlock()
 
3202
        new_branch.set_parent(branch.get_parent())
 
3203
        new_branch.set_bound_location(branch.get_bound_location())
 
3204
        new_branch.set_push_location(branch.get_push_location())
3171
3205
 
3172
3206
        # New branch has no tags by default
3173
3207
        new_branch.tags._set_tag_dict({})
3174
3208
 
3175
3209
        # Copying done; now update target format
3176
3210
        new_branch._transport.put_bytes('format',
3177
 
            format.as_string(),
 
3211
            format.get_format_string(),
3178
3212
            mode=new_branch.bzrdir._get_file_mode())
3179
3213
 
3180
3214
        # Clean up old files
3181
3215
        new_branch._transport.delete('revision-history')
3182
 
        branch.lock_write()
3183
3216
        try:
3184
 
            try:
3185
 
                branch.set_parent(None)
3186
 
            except errors.NoSuchFile:
3187
 
                pass
3188
 
            branch.set_bound_location(None)
3189
 
        finally:
3190
 
            branch.unlock()
 
3217
            branch.set_parent(None)
 
3218
        except errors.NoSuchFile:
 
3219
            pass
 
3220
        branch.set_bound_location(None)
3191
3221
 
3192
3222
 
3193
3223
class Converter6to7(object):
3197
3227
        format = BzrBranchFormat7()
3198
3228
        branch._set_config_location('stacked_on_location', '')
3199
3229
        # update target format
3200
 
        branch._transport.put_bytes('format', format.as_string())
 
3230
        branch._transport.put_bytes('format', format.get_format_string())
3201
3231
 
3202
3232
 
3203
3233
class Converter7to8(object):
3204
 
    """Perform an in-place upgrade of format 7 to format 8"""
 
3234
    """Perform an in-place upgrade of format 6 to format 7"""
3205
3235
 
3206
3236
    def convert(self, branch):
3207
3237
        format = BzrBranchFormat8()
3208
3238
        branch._transport.put_bytes('references', '')
3209
3239
        # update target format
3210
 
        branch._transport.put_bytes('format', format.as_string())
 
3240
        branch._transport.put_bytes('format', format.get_format_string())
 
3241
 
 
3242
 
 
3243
def _run_with_write_locked_target(target, callable, *args, **kwargs):
 
3244
    """Run ``callable(*args, **kwargs)``, write-locking target for the
 
3245
    duration.
 
3246
 
 
3247
    _run_with_write_locked_target will attempt to release the lock it acquires.
 
3248
 
 
3249
    If an exception is raised by callable, then that exception *will* be
 
3250
    propagated, even if the unlock attempt raises its own error.  Thus
 
3251
    _run_with_write_locked_target should be preferred to simply doing::
 
3252
 
 
3253
        target.lock_write()
 
3254
        try:
 
3255
            return callable(*args, **kwargs)
 
3256
        finally:
 
3257
            target.unlock()
 
3258
 
 
3259
    """
 
3260
    # This is very similar to bzrlib.decorators.needs_write_lock.  Perhaps they
 
3261
    # should share code?
 
3262
    target.lock_write()
 
3263
    try:
 
3264
        result = callable(*args, **kwargs)
 
3265
    except:
 
3266
        exc_info = sys.exc_info()
 
3267
        try:
 
3268
            target.unlock()
 
3269
        finally:
 
3270
            raise exc_info[0], exc_info[1], exc_info[2]
 
3271
    else:
 
3272
        target.unlock()
 
3273
        return result
3211
3274
 
3212
3275
 
3213
3276
class InterBranch(InterObject):
3243
3306
        raise NotImplementedError(self.pull)
3244
3307
 
3245
3308
    @needs_write_lock
3246
 
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
3309
    def update_revisions(self, stop_revision=None, overwrite=False,
 
3310
                         graph=None):
 
3311
        """Pull in new perfect-fit revisions.
 
3312
 
 
3313
        :param stop_revision: Updated until the given revision
 
3314
        :param overwrite: Always set the branch pointer, rather than checking
 
3315
            to see if it is a proper descendant.
 
3316
        :param graph: A Graph object that can be used to query history
 
3317
            information. This can be None.
 
3318
        :return: None
 
3319
        """
 
3320
        raise NotImplementedError(self.update_revisions)
 
3321
 
 
3322
    @needs_write_lock
 
3323
    def push(self, overwrite=False, stop_revision=None,
3247
3324
             _override_hook_source_branch=None):
3248
3325
        """Mirror the source branch into the target branch.
3249
3326
 
3260
3337
        """
3261
3338
        raise NotImplementedError(self.copy_content_into)
3262
3339
 
3263
 
    @needs_write_lock
3264
 
    def fetch(self, stop_revision=None, limit=None):
3265
 
        """Fetch revisions.
3266
 
 
3267
 
        :param stop_revision: Last revision to fetch
3268
 
        :param limit: Optional rough limit of revisions to fetch
3269
 
        """
3270
 
        raise NotImplementedError(self.fetch)
3271
 
 
3272
 
 
3273
 
def _fix_overwrite_type(overwrite):
3274
 
    if isinstance(overwrite, bool):
3275
 
        if overwrite:
3276
 
            return ["history", "tags"]
3277
 
        else:
3278
 
            return []
3279
 
    return overwrite
3280
 
 
3281
3340
 
3282
3341
class GenericInterBranch(InterBranch):
3283
3342
    """InterBranch implementation that uses public Branch functions."""
3289
3348
 
3290
3349
    @classmethod
3291
3350
    def _get_branch_formats_to_test(klass):
3292
 
        return [(format_registry.get_default(), format_registry.get_default())]
 
3351
        return [(BranchFormat._default_format, BranchFormat._default_format)]
3293
3352
 
3294
3353
    @classmethod
3295
3354
    def unwrap_format(klass, format):
3318
3377
            self.source.tags.merge_to(self.target.tags)
3319
3378
 
3320
3379
    @needs_write_lock
3321
 
    def fetch(self, stop_revision=None, limit=None):
3322
 
        if self.target.base == self.source.base:
3323
 
            return (0, [])
3324
 
        self.source.lock_read()
3325
 
        try:
3326
 
            fetch_spec_factory = fetch.FetchSpecFactory()
3327
 
            fetch_spec_factory.source_branch = self.source
3328
 
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3329
 
            fetch_spec_factory.source_repo = self.source.repository
3330
 
            fetch_spec_factory.target_repo = self.target.repository
3331
 
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3332
 
            fetch_spec_factory.limit = limit
3333
 
            fetch_spec = fetch_spec_factory.make_fetch_spec()
3334
 
            return self.target.repository.fetch(self.source.repository,
3335
 
                fetch_spec=fetch_spec)
3336
 
        finally:
3337
 
            self.source.unlock()
3338
 
 
3339
 
    @needs_write_lock
3340
 
    def _update_revisions(self, stop_revision=None, overwrite=False,
3341
 
            graph=None):
 
3380
    def update_revisions(self, stop_revision=None, overwrite=False,
 
3381
        graph=None):
 
3382
        """See InterBranch.update_revisions()."""
3342
3383
        other_revno, other_last_revision = self.source.last_revision_info()
3343
3384
        stop_revno = None # unknown
3344
3385
        if stop_revision is None:
3355
3396
        # case of having something to pull, and so that the check for
3356
3397
        # already merged can operate on the just fetched graph, which will
3357
3398
        # be cached in memory.
3358
 
        self.fetch(stop_revision=stop_revision)
 
3399
        self.target.fetch(self.source, stop_revision)
3359
3400
        # Check to see if one is an ancestor of the other
3360
3401
        if not overwrite:
3361
3402
            if graph is None:
3389
3430
        if local and not bound_location:
3390
3431
            raise errors.LocalRequiresBoundBranch()
3391
3432
        master_branch = None
3392
 
        source_is_master = False
3393
 
        if bound_location:
3394
 
            # bound_location comes from a config file, some care has to be
3395
 
            # taken to relate it to source.user_url
3396
 
            normalized = urlutils.normalize_url(bound_location)
3397
 
            try:
3398
 
                relpath = self.source.user_transport.relpath(normalized)
3399
 
                source_is_master = (relpath == '')
3400
 
            except (errors.PathNotChild, errors.InvalidURL):
3401
 
                source_is_master = False
3402
 
        if not local and bound_location and not source_is_master:
 
3433
        if not local and bound_location and self.source.user_url != bound_location:
3403
3434
            # not pulling from master, so we need to update master.
3404
3435
            master_branch = self.target.get_master_branch(possible_transports)
3405
3436
            master_branch.lock_write()
3411
3442
            return self._pull(overwrite,
3412
3443
                stop_revision, _hook_master=master_branch,
3413
3444
                run_hooks=run_hooks,
3414
 
                _override_hook_target=_override_hook_target,
3415
 
                merge_tags_to_master=not source_is_master)
 
3445
                _override_hook_target=_override_hook_target)
3416
3446
        finally:
3417
3447
            if master_branch:
3418
3448
                master_branch.unlock()
3419
3449
 
3420
 
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
3450
    def push(self, overwrite=False, stop_revision=None,
3421
3451
             _override_hook_source_branch=None):
3422
3452
        """See InterBranch.push.
3423
3453
 
3424
3454
        This is the basic concrete implementation of push()
3425
3455
 
3426
 
        :param _override_hook_source_branch: If specified, run the hooks
3427
 
            passing this Branch as the source, rather than self.  This is for
3428
 
            use of RemoteBranch, where push is delegated to the underlying
3429
 
            vfs-based Branch.
 
3456
        :param _override_hook_source_branch: If specified, run
 
3457
        the hooks passing this Branch as the source, rather than self.
 
3458
        This is for use of RemoteBranch, where push is delegated to the
 
3459
        underlying vfs-based Branch.
3430
3460
        """
3431
 
        if lossy:
3432
 
            raise errors.LossyPushToSameVCS(self.source, self.target)
3433
3461
        # TODO: Public option to disable running hooks - should be trivial but
3434
3462
        # needs tests.
3435
 
 
3436
 
        op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3437
 
        op.add_cleanup(self.source.lock_read().unlock)
3438
 
        op.add_cleanup(self.target.lock_write().unlock)
3439
 
        return op.run(overwrite, stop_revision,
3440
 
            _override_hook_source_branch=_override_hook_source_branch)
3441
 
 
3442
 
    def _basic_push(self, overwrite, stop_revision):
3443
 
        """Basic implementation of push without bound branches or hooks.
3444
 
 
3445
 
        Must be called with source read locked and target write locked.
3446
 
        """
3447
 
        result = BranchPushResult()
3448
 
        result.source_branch = self.source
3449
 
        result.target_branch = self.target
3450
 
        result.old_revno, result.old_revid = self.target.last_revision_info()
3451
 
        self.source.update_references(self.target)
3452
 
        overwrite = _fix_overwrite_type(overwrite)
3453
 
        if result.old_revid != stop_revision:
3454
 
            # We assume that during 'push' this repository is closer than
3455
 
            # the target.
3456
 
            graph = self.source.repository.get_graph(self.target.repository)
3457
 
            self._update_revisions(stop_revision,
3458
 
                overwrite=("history" in overwrite),
3459
 
                graph=graph)
3460
 
        if self.source._push_should_merge_tags():
3461
 
            result.tag_updates, result.tag_conflicts = (
3462
 
                self.source.tags.merge_to(
3463
 
                self.target.tags, "tags" in overwrite))
3464
 
        result.new_revno, result.new_revid = self.target.last_revision_info()
3465
 
        return result
3466
 
 
3467
 
    def _push_with_bound_branches(self, operation, overwrite, stop_revision,
 
3463
        self.source.lock_read()
 
3464
        try:
 
3465
            return _run_with_write_locked_target(
 
3466
                self.target, self._push_with_bound_branches, overwrite,
 
3467
                stop_revision,
 
3468
                _override_hook_source_branch=_override_hook_source_branch)
 
3469
        finally:
 
3470
            self.source.unlock()
 
3471
 
 
3472
    def _push_with_bound_branches(self, overwrite, stop_revision,
3468
3473
            _override_hook_source_branch=None):
3469
3474
        """Push from source into target, and into target's master if any.
3470
3475
        """
3482
3487
            # be bound to itself? -- mbp 20070507
3483
3488
            master_branch = self.target.get_master_branch()
3484
3489
            master_branch.lock_write()
3485
 
            operation.add_cleanup(master_branch.unlock)
3486
 
            # push into the master from the source branch.
3487
 
            master_inter = InterBranch.get(self.source, master_branch)
3488
 
            master_inter._basic_push(overwrite, stop_revision)
3489
 
            # and push into the target branch from the source. Note that
3490
 
            # we push from the source branch again, because it's considered
3491
 
            # the highest bandwidth repository.
3492
 
            result = self._basic_push(overwrite, stop_revision)
3493
 
            result.master_branch = master_branch
3494
 
            result.local_branch = self.target
 
3490
            try:
 
3491
                # push into the master from the source branch.
 
3492
                self.source._basic_push(master_branch, overwrite, stop_revision)
 
3493
                # and push into the target branch from the source. Note that we
 
3494
                # push from the source branch again, because it's considered the
 
3495
                # highest bandwidth repository.
 
3496
                result = self.source._basic_push(self.target, overwrite,
 
3497
                    stop_revision)
 
3498
                result.master_branch = master_branch
 
3499
                result.local_branch = self.target
 
3500
                _run_hooks()
 
3501
                return result
 
3502
            finally:
 
3503
                master_branch.unlock()
3495
3504
        else:
3496
 
            master_branch = None
3497
3505
            # no master branch
3498
 
            result = self._basic_push(overwrite, stop_revision)
 
3506
            result = self.source._basic_push(self.target, overwrite,
 
3507
                stop_revision)
3499
3508
            # TODO: Why set master_branch and local_branch if there's no
3500
3509
            # binding?  Maybe cleaner to just leave them unset? -- mbp
3501
3510
            # 20070504
3502
3511
            result.master_branch = self.target
3503
3512
            result.local_branch = None
3504
 
        _run_hooks()
3505
 
        return result
 
3513
            _run_hooks()
 
3514
            return result
3506
3515
 
3507
3516
    def _pull(self, overwrite=False, stop_revision=None,
3508
3517
             possible_transports=None, _hook_master=None, run_hooks=True,
3509
 
             _override_hook_target=None, local=False,
3510
 
             merge_tags_to_master=True):
 
3518
             _override_hook_target=None, local=False):
3511
3519
        """See Branch.pull.
3512
3520
 
3513
3521
        This function is the core worker, used by GenericInterBranch.pull to
3518
3526
        :param run_hooks: Private parameter - if false, this branch
3519
3527
            is being called because it's the master of the primary branch,
3520
3528
            so it should not run its hooks.
3521
 
            is being called because it's the master of the primary branch,
3522
 
            so it should not run its hooks.
3523
3529
        :param _override_hook_target: Private parameter - set the branch to be
3524
3530
            supplied as the target_branch to pull hooks.
3525
3531
        :param local: Only update the local branch, and not the bound branch.
3544
3550
            # -- JRV20090506
3545
3551
            result.old_revno, result.old_revid = \
3546
3552
                self.target.last_revision_info()
3547
 
            overwrite = _fix_overwrite_type(overwrite)
3548
 
            self._update_revisions(stop_revision,
3549
 
                overwrite=("history" in overwrite),
3550
 
                graph=graph)
 
3553
            self.target.update_revisions(self.source, stop_revision,
 
3554
                overwrite=overwrite, graph=graph)
3551
3555
            # TODO: The old revid should be specified when merging tags, 
3552
3556
            # so a tags implementation that versions tags can only 
3553
3557
            # pull in the most recent changes. -- JRV20090506
3554
 
            result.tag_updates, result.tag_conflicts = (
3555
 
                self.source.tags.merge_to(self.target.tags,
3556
 
                    "tags" in overwrite,
3557
 
                    ignore_master=not merge_tags_to_master))
 
3558
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3559
                overwrite)
3558
3560
            result.new_revno, result.new_revid = self.target.last_revision_info()
3559
3561
            if _hook_master:
3560
3562
                result.master_branch = _hook_master