~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Andrew Bennetts
  • Date: 2010-07-29 11:17:57 UTC
  • mfrom: (5050.3.17 2.2)
  • mto: This revision was merged to the branch mainline in revision 5365.
  • Revision ID: andrew.bennetts@canonical.com-20100729111757-018h3pcefo7z0dnq
Merge lp:bzr/2.2 into lp:bzr.

Show diffs side-by-side

added added

removed removed

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