~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-07-29 10:44:42 UTC
  • mfrom: (5361.1.1 2.3-lp-home)
  • Revision ID: pqm@pqm.ubuntu.com-20100729104442-5g1m4pumcss037ic
(spiv) Expand lp:~/ to lp:~username/ if a user has already logged in. (John
 A Meinel)

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()
269
246
        if not local and not config.has_explicit_nickname():
270
247
            try:
271
248
                master = self.get_master_branch(possible_transports)
272
 
                if master and self.user_url == master.user_url:
273
 
                    raise errors.RecursiveBind(self.user_url)
274
249
                if master is not None:
275
250
                    # return the master branch value
276
251
                    return master.nick
277
 
            except errors.RecursiveBind, e:
278
 
                raise e
279
252
            except errors.BzrError, e:
280
253
                # Silently fall back to local implicit nick if the master is
281
254
                # unavailable
458
431
            after. If None, the rest of history is included.
459
432
        :param stop_rule: if stop_revision_id is not None, the precise rule
460
433
            to use for termination:
461
 
 
462
434
            * 'exclude' - leave the stop revision out of the result (default)
463
435
            * 'include' - the stop revision is the last item in the result
464
436
            * 'with-merges' - include the stop revision and all of its
466
438
            * 'with-merges-without-common-ancestry' - filter out revisions 
467
439
              that are in both ancestries
468
440
        :param direction: either 'reverse' or 'forward':
469
 
 
470
441
            * reverse means return the start_revision_id first, i.e.
471
442
              start at the most recent revision and go backwards in history
472
443
            * forward returns tuples in the opposite order to reverse.
516
487
        rev_iter = iter(merge_sorted_revisions)
517
488
        if start_revision_id is not None:
518
489
            for node in rev_iter:
519
 
                rev_id = node.key
 
490
                rev_id = node.key[-1]
520
491
                if rev_id != start_revision_id:
521
492
                    continue
522
493
                else:
523
494
                    # The decision to include the start or not
524
495
                    # depends on the stop_rule if a stop is provided
525
496
                    # so pop this node back into the iterator
526
 
                    rev_iter = itertools.chain(iter([node]), rev_iter)
 
497
                    rev_iter = chain(iter([node]), rev_iter)
527
498
                    break
528
499
        if stop_revision_id is None:
529
500
            # Yield everything
530
501
            for node in rev_iter:
531
 
                rev_id = node.key
 
502
                rev_id = node.key[-1]
532
503
                yield (rev_id, node.merge_depth, node.revno,
533
504
                       node.end_of_merge)
534
505
        elif stop_rule == 'exclude':
535
506
            for node in rev_iter:
536
 
                rev_id = node.key
 
507
                rev_id = node.key[-1]
537
508
                if rev_id == stop_revision_id:
538
509
                    return
539
510
                yield (rev_id, node.merge_depth, node.revno,
540
511
                       node.end_of_merge)
541
512
        elif stop_rule == 'include':
542
513
            for node in rev_iter:
543
 
                rev_id = node.key
 
514
                rev_id = node.key[-1]
544
515
                yield (rev_id, node.merge_depth, node.revno,
545
516
                       node.end_of_merge)
546
517
                if rev_id == stop_revision_id:
552
523
            ancestors = graph.find_unique_ancestors(start_revision_id,
553
524
                                                    [stop_revision_id])
554
525
            for node in rev_iter:
555
 
                rev_id = node.key
 
526
                rev_id = node.key[-1]
556
527
                if rev_id not in ancestors:
557
528
                    continue
558
529
                yield (rev_id, node.merge_depth, node.revno,
568
539
            reached_stop_revision_id = False
569
540
            revision_id_whitelist = []
570
541
            for node in rev_iter:
571
 
                rev_id = node.key
 
542
                rev_id = node.key[-1]
572
543
                if rev_id == left_parent:
573
544
                    # reached the left parent after the stop_revision
574
545
                    return
654
625
        """
655
626
        raise errors.UpgradeRequired(self.user_url)
656
627
 
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
628
    def set_append_revisions_only(self, enabled):
666
629
        if not self._format.supports_set_append_revisions_only():
667
630
            raise errors.UpgradeRequired(self.user_url)
681
644
        raise errors.UnsupportedOperation(self.get_reference_info, self)
682
645
 
683
646
    @needs_write_lock
684
 
    def fetch(self, from_branch, last_revision=None, limit=None):
 
647
    def fetch(self, from_branch, last_revision=None, pb=None):
685
648
        """Copy revisions from from_branch into this branch.
686
649
 
687
650
        :param from_branch: Where to copy from.
688
651
        :param last_revision: What revision to stop at (None for at the end
689
652
                              of the branch.
690
 
        :param limit: Optional rough limit of revisions to fetch
 
653
        :param pb: An optional progress bar to use.
691
654
        :return: None
692
655
        """
693
 
        return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
 
656
        if self.base == from_branch.base:
 
657
            return (0, [])
 
658
        if pb is not None:
 
659
            symbol_versioning.warn(
 
660
                symbol_versioning.deprecated_in((1, 14, 0))
 
661
                % "pb parameter to fetch()")
 
662
        from_branch.lock_read()
 
663
        try:
 
664
            if last_revision is None:
 
665
                last_revision = from_branch.last_revision()
 
666
                last_revision = _mod_revision.ensure_null(last_revision)
 
667
            return self.repository.fetch(from_branch.repository,
 
668
                                         revision_id=last_revision,
 
669
                                         pb=pb)
 
670
        finally:
 
671
            from_branch.unlock()
694
672
 
695
673
    def get_bound_location(self):
696
674
        """Return the URL of the branch we are bound to.
707
685
 
708
686
    def get_commit_builder(self, parents, config=None, timestamp=None,
709
687
                           timezone=None, committer=None, revprops=None,
710
 
                           revision_id=None, lossy=False):
 
688
                           revision_id=None):
711
689
        """Obtain a CommitBuilder for this branch.
712
690
 
713
691
        :param parents: Revision ids of the parents of the new revision.
717
695
        :param committer: Optional committer to set for commit.
718
696
        :param revprops: Optional dictionary of revision properties.
719
697
        :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
698
        """
723
699
 
724
700
        if config is None:
725
701
            config = self.get_config()
726
702
 
727
703
        return self.repository.get_commit_builder(self, parents, config,
728
 
            timestamp, timezone, committer, revprops, revision_id,
729
 
            lossy)
 
704
            timestamp, timezone, committer, revprops, revision_id)
730
705
 
731
706
    def get_master_branch(self, possible_transports=None):
732
707
        """Return the branch we are bound to.
735
710
        """
736
711
        return None
737
712
 
738
 
    @deprecated_method(deprecated_in((2, 5, 0)))
739
713
    def get_revision_delta(self, revno):
740
714
        """Return the delta for one revision.
741
715
 
742
716
        The delta is relative to its mainline predecessor, or the
743
717
        empty tree for revision 1.
744
718
        """
745
 
        try:
746
 
            revid = self.get_rev_id(revno)
747
 
        except errors.NoSuchRevision:
 
719
        rh = self.revision_history()
 
720
        if not (1 <= revno <= len(rh)):
748
721
            raise errors.InvalidRevisionNumber(revno)
749
 
        return self.repository.get_revision_delta(revid)
 
722
        return self.repository.get_revision_delta(rh[revno-1])
750
723
 
751
724
    def get_stacked_on_url(self):
752
725
        """Get the URL this branch is stacked against.
761
734
        """Print `file` to stdout."""
762
735
        raise NotImplementedError(self.print_file)
763
736
 
764
 
    @deprecated_method(deprecated_in((2, 4, 0)))
765
737
    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)
 
738
        raise NotImplementedError(self.set_revision_history)
812
739
 
813
740
    @needs_write_lock
814
741
    def set_parent(self, url):
858
785
 
859
786
    def _unstack(self):
860
787
        """Change a branch to be unstacked, copying data as needed.
861
 
 
 
788
        
862
789
        Don't call this directly, use set_stacked_on_url(None).
863
790
        """
864
791
        pb = ui.ui_factory.nested_progress_bar()
865
792
        try:
866
 
            pb.update(gettext("Unstacking"))
 
793
            pb.update("Unstacking")
867
794
            # The basic approach here is to fetch the tip of the branch,
868
795
            # including all available ghosts, from the existing stacked
869
796
            # repository into a new repository object without the fallbacks. 
873
800
            old_repository = self.repository
874
801
            if len(old_repository._fallback_repositories) != 1:
875
802
                raise AssertionError("can't cope with fallback repositories "
876
 
                    "of %r (fallbacks: %r)" % (old_repository,
877
 
                        old_repository._fallback_repositories))
 
803
                    "of %r" % (self.repository,))
878
804
            # Open the new repository object.
879
805
            # Repositories don't offer an interface to remove fallback
880
806
            # repositories today; take the conceptually simpler option and just
883
809
            # stream from one of them to the other.  This does mean doing
884
810
            # separate SSH connection setup, but unstacking is not a
885
811
            # common operation so it's tolerable.
886
 
            new_bzrdir = controldir.ControlDir.open(
887
 
                self.bzrdir.root_transport.base)
 
812
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
888
813
            new_repository = new_bzrdir.find_repository()
889
814
            if new_repository._fallback_repositories:
890
815
                raise AssertionError("didn't expect %r to have "
929
854
                # XXX: If you unstack a branch while it has a working tree
930
855
                # with a pending merge, the pending-merged revisions will no
931
856
                # 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)
 
857
                #
 
858
                # XXX: This only fetches up to the tip of the repository; it
 
859
                # doesn't bring across any tags.  That's fairly consistent
 
860
                # with how branch works, but perhaps not ideal.
 
861
                self.repository.fetch(old_repository,
 
862
                    revision_id=self.last_revision(),
 
863
                    find_ghosts=True)
940
864
            finally:
941
865
                old_repository.unlock()
942
866
        finally:
947
871
 
948
872
        :seealso: Branch._get_tags_bytes.
949
873
        """
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)
 
874
        return _run_with_write_locked_target(self, self._transport.put_bytes,
 
875
            'tags', bytes)
957
876
 
958
877
    def _cache_revision_history(self, rev_history):
959
878
        """Set the cached revision history to rev_history.
986
905
        self._revision_history_cache = None
987
906
        self._revision_id_to_revno_cache = None
988
907
        self._last_revision_info_cache = None
989
 
        self._master_branch_cache = None
990
908
        self._merge_sorted_revisions_cache = None
991
909
        self._partial_revision_history_cache = []
992
910
        self._partial_revision_id_to_revno_cache = {}
993
 
        self._tags_bytes = None
994
911
 
995
912
    def _gen_revision_history(self):
996
913
        """Return sequence of revision hashes on to this branch.
1007
924
        """
1008
925
        raise NotImplementedError(self._gen_revision_history)
1009
926
 
1010
 
    @deprecated_method(deprecated_in((2, 5, 0)))
1011
927
    @needs_read_lock
1012
928
    def revision_history(self):
1013
929
        """Return sequence of revision ids on this branch.
1015
931
        This method will cache the revision history for as long as it is safe to
1016
932
        do so.
1017
933
        """
1018
 
        return self._revision_history()
1019
 
 
1020
 
    def _revision_history(self):
1021
934
        if 'evil' in debug.debug_flags:
1022
935
            mutter_callsite(3, "revision_history scales with history.")
1023
936
        if self._revision_history_cache is not None:
1050
963
        :return: A tuple (revno, revision_id).
1051
964
        """
1052
965
        if self._last_revision_info_cache is None:
1053
 
            self._last_revision_info_cache = self._read_last_revision_info()
 
966
            self._last_revision_info_cache = self._last_revision_info()
1054
967
        return self._last_revision_info_cache
1055
968
 
1056
 
    def _read_last_revision_info(self):
1057
 
        raise NotImplementedError(self._read_last_revision_info)
1058
 
 
1059
 
    @deprecated_method(deprecated_in((2, 4, 0)))
 
969
    def _last_revision_info(self):
 
970
        rh = self.revision_history()
 
971
        revno = len(rh)
 
972
        if revno:
 
973
            return (revno, rh[-1])
 
974
        else:
 
975
            return (0, _mod_revision.NULL_REVISION)
 
976
 
 
977
    @deprecated_method(deprecated_in((1, 6, 0)))
 
978
    def missing_revisions(self, other, stop_revision=None):
 
979
        """Return a list of new revisions that would perfectly fit.
 
980
 
 
981
        If self and other have not diverged, return a list of the revisions
 
982
        present in other, but missing from self.
 
983
        """
 
984
        self_history = self.revision_history()
 
985
        self_len = len(self_history)
 
986
        other_history = other.revision_history()
 
987
        other_len = len(other_history)
 
988
        common_index = min(self_len, other_len) -1
 
989
        if common_index >= 0 and \
 
990
            self_history[common_index] != other_history[common_index]:
 
991
            raise errors.DivergedBranches(self, other)
 
992
 
 
993
        if stop_revision is None:
 
994
            stop_revision = other_len
 
995
        else:
 
996
            if stop_revision > other_len:
 
997
                raise errors.NoSuchRevision(self, stop_revision)
 
998
        return other_history[self_len:stop_revision]
 
999
 
 
1000
    def update_revisions(self, other, stop_revision=None, overwrite=False,
 
1001
                         graph=None):
 
1002
        """Pull in new perfect-fit revisions.
 
1003
 
 
1004
        :param other: Another Branch to pull from
 
1005
        :param stop_revision: Updated until the given revision
 
1006
        :param overwrite: Always set the branch pointer, rather than checking
 
1007
            to see if it is a proper descendant.
 
1008
        :param graph: A Graph object that can be used to query history
 
1009
            information. This can be None.
 
1010
        :return: None
 
1011
        """
 
1012
        return InterBranch.get(other, self).update_revisions(stop_revision,
 
1013
            overwrite, graph)
 
1014
 
1060
1015
    def import_last_revision_info(self, source_repo, revno, revid):
1061
1016
        """Set the last revision info, importing from another repo if necessary.
1062
1017
 
 
1018
        This is used by the bound branch code to upload a revision to
 
1019
        the master branch first before updating the tip of the local branch.
 
1020
 
1063
1021
        :param source_repo: Source repository to optionally fetch from
1064
1022
        :param revno: Revision number of the new tip
1065
1023
        :param revid: Revision id of the new tip
1068
1026
            self.repository.fetch(source_repo, revision_id=revid)
1069
1027
        self.set_last_revision_info(revno, revid)
1070
1028
 
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
1029
    def revision_id_to_revno(self, revision_id):
1093
1030
        """Given a revision id, return its revno"""
1094
1031
        if _mod_revision.is_null(revision_id):
1095
1032
            return 0
1096
 
        history = self._revision_history()
 
1033
        history = self.revision_history()
1097
1034
        try:
1098
1035
            return history.index(revision_id) + 1
1099
1036
        except ValueError:
1126
1063
            stop_revision=stop_revision,
1127
1064
            possible_transports=possible_transports, *args, **kwargs)
1128
1065
 
1129
 
    def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1130
 
            *args, **kwargs):
 
1066
    def push(self, target, overwrite=False, stop_revision=None, *args,
 
1067
        **kwargs):
1131
1068
        """Mirror this branch into target.
1132
1069
 
1133
1070
        This branch is considered to be 'local', having low latency.
1134
1071
        """
1135
1072
        return InterBranch.get(self, target).push(overwrite, stop_revision,
1136
 
            lossy, *args, **kwargs)
 
1073
            *args, **kwargs)
 
1074
 
 
1075
    def lossy_push(self, target, stop_revision=None):
 
1076
        """Push deltas into another branch.
 
1077
 
 
1078
        :note: This does not, like push, retain the revision ids from 
 
1079
            the source branch and will, rather than adding bzr-specific 
 
1080
            metadata, push only those semantics of the revision that can be 
 
1081
            natively represented by this branch' VCS.
 
1082
 
 
1083
        :param target: Target branch
 
1084
        :param stop_revision: Revision to push, defaults to last revision.
 
1085
        :return: BranchPushResult with an extra member revidmap: 
 
1086
            A dictionary mapping revision ids from the target branch 
 
1087
            to new revision ids in the target branch, for each 
 
1088
            revision that was pushed.
 
1089
        """
 
1090
        inter = InterBranch.get(self, target)
 
1091
        lossy_push = getattr(inter, "lossy_push", None)
 
1092
        if lossy_push is None:
 
1093
            raise errors.LossyPushToSameVCS(self, target)
 
1094
        return lossy_push(stop_revision)
1137
1095
 
1138
1096
    def basis_tree(self):
1139
1097
        """Return `Tree` object for last revision."""
1294
1252
        return result
1295
1253
 
1296
1254
    @needs_read_lock
1297
 
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
1298
 
            repository=None):
 
1255
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
1299
1256
        """Create a new line of development from the branch, into to_bzrdir.
1300
1257
 
1301
1258
        to_bzrdir controls the branch format.
1306
1263
        if (repository_policy is not None and
1307
1264
            repository_policy.requires_stacking()):
1308
1265
            to_bzrdir._format.require_stacking(_skip_repo=True)
1309
 
        result = to_bzrdir.create_branch(repository=repository)
 
1266
        result = to_bzrdir.create_branch()
1310
1267
        result.lock_write()
1311
1268
        try:
1312
1269
            if repository_policy is not None:
1313
1270
                repository_policy.configure_branch(result)
1314
1271
            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)
 
1272
            result.set_parent(self.bzrdir.root_transport.base)
1320
1273
        finally:
1321
1274
            result.unlock()
1322
1275
        return result
1396
1349
        # TODO: We should probably also check that self.revision_history
1397
1350
        # matches the repository for older branch formats.
1398
1351
        # If looking for the code that cross-checks repository parents against
1399
 
        # the Graph.iter_lefthand_ancestry output, that is now a repository
 
1352
        # the iter_reverse_revision_history output, that is now a repository
1400
1353
        # specific check.
1401
1354
        return result
1402
1355
 
1403
 
    def _get_checkout_format(self, lightweight=False):
 
1356
    def _get_checkout_format(self):
1404
1357
        """Return the most suitable metadir for a checkout of this branch.
1405
1358
        Weaves are used if this branch's repository uses weaves.
1406
1359
        """
1407
 
        format = self.repository.bzrdir.checkout_metadir()
1408
 
        format.set_branch_format(self._format)
 
1360
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
 
1361
            from bzrlib.repofmt import weaverepo
 
1362
            format = bzrdir.BzrDirMetaFormat1()
 
1363
            format.repository_format = weaverepo.RepositoryFormat7()
 
1364
        else:
 
1365
            format = self.repository.bzrdir.checkout_metadir()
 
1366
            format.set_branch_format(self._format)
1409
1367
        return format
1410
1368
 
1411
1369
    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):
 
1370
        stacked_on=None, create_prefix=False, use_existing_dir=False):
1414
1371
        """Create a clone of this branch and its bzrdir.
1415
1372
 
1416
1373
        :param to_transport: The transport to clone onto.
1429
1386
            revision_id = self.last_revision()
1430
1387
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1431
1388
            revision_id=revision_id, stacked_on=stacked_on,
1432
 
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
1433
 
            no_tree=no_tree)
 
1389
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1434
1390
        return dir_to.open_branch()
1435
1391
 
1436
1392
    def create_checkout(self, to_location, revision_id=None,
1441
1397
        :param to_location: The url to produce the checkout at
1442
1398
        :param revision_id: The revision to check out
1443
1399
        :param lightweight: If True, produce a lightweight checkout, otherwise,
1444
 
            produce a bound branch (heavyweight checkout)
 
1400
        produce a bound branch (heavyweight checkout)
1445
1401
        :param accelerator_tree: A tree which can be used for retrieving file
1446
1402
            contents more quickly than the revision tree, i.e. a workingtree.
1447
1403
            The revision tree will be used for cases where accelerator_tree's
1452
1408
        """
1453
1409
        t = transport.get_transport(to_location)
1454
1410
        t.ensure_base()
1455
 
        format = self._get_checkout_format(lightweight=lightweight)
1456
1411
        if lightweight:
 
1412
            format = self._get_checkout_format()
1457
1413
            checkout = format.initialize_on_transport(t)
1458
1414
            from_branch = BranchReferenceFormat().initialize(checkout, 
1459
1415
                target_branch=self)
1460
1416
        else:
1461
 
            checkout_branch = controldir.ControlDir.create_branch_convenience(
 
1417
            format = self._get_checkout_format()
 
1418
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1462
1419
                to_location, force_new_tree=False, format=format)
1463
1420
            checkout = checkout_branch.bzrdir
1464
1421
            checkout_branch.bind(self)
1492
1449
 
1493
1450
    def reference_parent(self, file_id, path, possible_transports=None):
1494
1451
        """Return the parent branch for a tree-reference file_id
1495
 
 
1496
1452
        :param file_id: The file_id of the tree reference
1497
1453
        :param path: The path of the file_id in the tree
1498
1454
        :return: A branch associated with the file_id
1551
1507
        else:
1552
1508
            raise AssertionError("invalid heads: %r" % (heads,))
1553
1509
 
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):
 
1510
 
 
1511
class BranchFormat(object):
1581
1512
    """An encapsulation of the initialization and open routines for a format.
1582
1513
 
1583
1514
    Formats provide three things:
1586
1517
     * an open routine.
1587
1518
 
1588
1519
    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
 
1520
    during branch opening. Its not required that these be instances, they
1590
1521
    can be classes themselves with class methods - it simply depends on
1591
1522
    whether state is needed for a given format or not.
1592
1523
 
1595
1526
    object will be created every time regardless.
1596
1527
    """
1597
1528
 
 
1529
    _default_format = None
 
1530
    """The default format used for new branches."""
 
1531
 
 
1532
    _formats = {}
 
1533
    """The known formats."""
 
1534
 
 
1535
    can_set_append_revisions_only = True
 
1536
 
1598
1537
    def __eq__(self, other):
1599
1538
        return self.__class__ is other.__class__
1600
1539
 
1602
1541
        return not (self == other)
1603
1542
 
1604
1543
    @classmethod
1605
 
    def find_format(klass, controldir, name=None):
1606
 
        """Return the format for the branch object in controldir."""
 
1544
    def find_format(klass, a_bzrdir, name=None):
 
1545
        """Return the format for the branch object in a_bzrdir."""
1607
1546
        try:
1608
 
            transport = controldir.get_branch_transport(None, name=name)
 
1547
            transport = a_bzrdir.get_branch_transport(None, name=name)
1609
1548
            format_string = transport.get_bytes("format")
1610
 
            return format_registry.get(format_string)
 
1549
            format = klass._formats[format_string]
 
1550
            if isinstance(format, MetaDirBranchFormatFactory):
 
1551
                return format()
 
1552
            return format
1611
1553
        except errors.NoSuchFile:
1612
 
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
 
1554
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1613
1555
        except KeyError:
1614
1556
            raise errors.UnknownFormatError(format=format_string, kind='branch')
1615
1557
 
1616
1558
    @classmethod
1617
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1618
1559
    def get_default_format(klass):
1619
1560
        """Return the current default format."""
1620
 
        return format_registry.get_default()
 
1561
        return klass._default_format
1621
1562
 
1622
1563
    @classmethod
1623
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1624
1564
    def get_formats(klass):
1625
1565
        """Get all the known formats.
1626
1566
 
1627
1567
        Warning: This triggers a load of all lazy registered formats: do not
1628
1568
        use except when that is desireed.
1629
1569
        """
1630
 
        return format_registry._get_all()
 
1570
        result = []
 
1571
        for fmt in klass._formats.values():
 
1572
            if isinstance(fmt, MetaDirBranchFormatFactory):
 
1573
                fmt = fmt()
 
1574
            result.append(fmt)
 
1575
        return result
1631
1576
 
1632
 
    def get_reference(self, controldir, name=None):
1633
 
        """Get the target reference of the branch in controldir.
 
1577
    def get_reference(self, a_bzrdir, name=None):
 
1578
        """Get the target reference of the branch in a_bzrdir.
1634
1579
 
1635
1580
        format probing must have been completed before calling
1636
1581
        this method - it is assumed that the format of the branch
1637
 
        in controldir is correct.
 
1582
        in a_bzrdir is correct.
1638
1583
 
1639
 
        :param controldir: The controldir to get the branch data from.
 
1584
        :param a_bzrdir: The bzrdir to get the branch data from.
1640
1585
        :param name: Name of the colocated branch to fetch
1641
1586
        :return: None if the branch is not a reference branch.
1642
1587
        """
1643
1588
        return None
1644
1589
 
1645
1590
    @classmethod
1646
 
    def set_reference(self, controldir, name, to_branch):
1647
 
        """Set the target reference of the branch in controldir.
 
1591
    def set_reference(self, a_bzrdir, name, to_branch):
 
1592
        """Set the target reference of the branch in a_bzrdir.
1648
1593
 
1649
1594
        format probing must have been completed before calling
1650
1595
        this method - it is assumed that the format of the branch
1651
 
        in controldir is correct.
 
1596
        in a_bzrdir is correct.
1652
1597
 
1653
 
        :param controldir: The controldir to set the branch reference for.
 
1598
        :param a_bzrdir: The bzrdir to set the branch reference for.
1654
1599
        :param name: Name of colocated branch to set, None for default
1655
1600
        :param to_branch: branch that the checkout is to reference
1656
1601
        """
1664
1609
        """Return the short format description for this format."""
1665
1610
        raise NotImplementedError(self.get_format_description)
1666
1611
 
1667
 
    def _run_post_branch_init_hooks(self, controldir, name, branch):
 
1612
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
1668
1613
        hooks = Branch.hooks['post_branch_init']
1669
1614
        if not hooks:
1670
1615
            return
1671
 
        params = BranchInitHookParams(self, controldir, name, branch)
 
1616
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
1672
1617
        for hook in hooks:
1673
1618
            hook(params)
1674
1619
 
1675
 
    def initialize(self, controldir, name=None, repository=None,
1676
 
                   append_revisions_only=None):
1677
 
        """Create a branch of this format in controldir.
1678
 
 
 
1620
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
1621
                           lock_type='metadir', set_format=True):
 
1622
        """Initialize a branch in a bzrdir, with specified files
 
1623
 
 
1624
        :param a_bzrdir: The bzrdir to initialize the branch in
 
1625
        :param utf8_files: The files to create as a list of
 
1626
            (filename, content) tuples
 
1627
        :param name: Name of colocated branch to create, if any
 
1628
        :param set_format: If True, set the format with
 
1629
            self.get_format_string.  (BzrBranch4 has its format set
 
1630
            elsewhere)
 
1631
        :return: a branch in this format
 
1632
        """
 
1633
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
1634
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
 
1635
        lock_map = {
 
1636
            'metadir': ('lock', lockdir.LockDir),
 
1637
            'branch4': ('branch-lock', lockable_files.TransportLock),
 
1638
        }
 
1639
        lock_name, lock_class = lock_map[lock_type]
 
1640
        control_files = lockable_files.LockableFiles(branch_transport,
 
1641
            lock_name, lock_class)
 
1642
        control_files.create_lock()
 
1643
        try:
 
1644
            control_files.lock_write()
 
1645
        except errors.LockContention:
 
1646
            if lock_type != 'branch4':
 
1647
                raise
 
1648
            lock_taken = False
 
1649
        else:
 
1650
            lock_taken = True
 
1651
        if set_format:
 
1652
            utf8_files += [('format', self.get_format_string())]
 
1653
        try:
 
1654
            for (filename, content) in utf8_files:
 
1655
                branch_transport.put_bytes(
 
1656
                    filename, content,
 
1657
                    mode=a_bzrdir._get_file_mode())
 
1658
        finally:
 
1659
            if lock_taken:
 
1660
                control_files.unlock()
 
1661
        branch = self.open(a_bzrdir, name, _found=True)
 
1662
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
1663
        return branch
 
1664
 
 
1665
    def initialize(self, a_bzrdir, name=None):
 
1666
        """Create a branch of this format in a_bzrdir.
 
1667
        
1679
1668
        :param name: Name of the colocated branch to create.
1680
1669
        """
1681
1670
        raise NotImplementedError(self.initialize)
1701
1690
        Note that it is normal for branch to be a RemoteBranch when using tags
1702
1691
        on a RemoteBranch.
1703
1692
        """
1704
 
        return _mod_tag.DisabledTags(branch)
 
1693
        return DisabledTags(branch)
1705
1694
 
1706
1695
    def network_name(self):
1707
1696
        """A simple byte string uniquely identifying this format for RPC calls.
1713
1702
        """
1714
1703
        raise NotImplementedError(self.network_name)
1715
1704
 
1716
 
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
1717
 
            found_repository=None):
1718
 
        """Return the branch object for controldir.
 
1705
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
1706
        """Return the branch object for a_bzrdir
1719
1707
 
1720
 
        :param controldir: A ControlDir that contains a branch.
 
1708
        :param a_bzrdir: A BzrDir that contains a branch.
1721
1709
        :param name: Name of colocated branch to open
1722
1710
        :param _found: a private parameter, do not use it. It is used to
1723
1711
            indicate if format probing has already be done.
1727
1715
        raise NotImplementedError(self.open)
1728
1716
 
1729
1717
    @classmethod
1730
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1731
1718
    def register_format(klass, format):
1732
1719
        """Register a metadir format.
1733
 
 
 
1720
        
1734
1721
        See MetaDirBranchFormatFactory for the ability to register a format
1735
1722
        without loading the code the format needs until it is actually used.
1736
1723
        """
1737
 
        format_registry.register(format)
 
1724
        klass._formats[format.get_format_string()] = format
 
1725
        # Metadir formats have a network name of their format string, and get
 
1726
        # registered as factories.
 
1727
        if isinstance(format, MetaDirBranchFormatFactory):
 
1728
            network_format_registry.register(format.get_format_string(), format)
 
1729
        else:
 
1730
            network_format_registry.register(format.get_format_string(),
 
1731
                format.__class__)
1738
1732
 
1739
1733
    @classmethod
1740
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1741
1734
    def set_default_format(klass, format):
1742
 
        format_registry.set_default(format)
 
1735
        klass._default_format = format
1743
1736
 
1744
1737
    def supports_set_append_revisions_only(self):
1745
1738
        """True if this format supports set_append_revisions_only."""
1749
1742
        """True if this format records a stacked-on branch."""
1750
1743
        return False
1751
1744
 
1752
 
    def supports_leaving_lock(self):
1753
 
        """True if this format supports leaving locks in place."""
1754
 
        return False # by default
1755
 
 
1756
1745
    @classmethod
1757
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1758
1746
    def unregister_format(klass, format):
1759
 
        format_registry.remove(format)
 
1747
        del klass._formats[format.get_format_string()]
1760
1748
 
1761
1749
    def __str__(self):
1762
1750
        return self.get_format_description().rstrip()
1765
1753
        """True if this format supports tags stored in the branch"""
1766
1754
        return False  # by default
1767
1755
 
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
1756
 
1777
1757
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1778
1758
    """A factory for a BranchFormat object, permitting simple lazy registration.
1815
1795
        These are all empty initially, because by default nothing should get
1816
1796
        notified.
1817
1797
        """
1818
 
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1819
 
        self.add_hook('set_rh',
 
1798
        Hooks.__init__(self)
 
1799
        self.create_hook(HookPoint('set_rh',
1820
1800
            "Invoked whenever the revision history has been set via "
1821
1801
            "set_revision_history. The api signature is (branch, "
1822
1802
            "revision_history), and the branch will be write-locked. "
1823
1803
            "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',
 
1804
            "hook to use is Branch.post_change_branch_tip.", (0, 15), None))
 
1805
        self.create_hook(HookPoint('open',
1826
1806
            "Called with the Branch object that has been opened after a "
1827
 
            "branch is opened.", (1, 8))
1828
 
        self.add_hook('post_push',
 
1807
            "branch is opened.", (1, 8), None))
 
1808
        self.create_hook(HookPoint('post_push',
1829
1809
            "Called after a push operation completes. post_push is called "
1830
1810
            "with a bzrlib.branch.BranchPushResult object and only runs in the "
1831
 
            "bzr client.", (0, 15))
1832
 
        self.add_hook('post_pull',
 
1811
            "bzr client.", (0, 15), None))
 
1812
        self.create_hook(HookPoint('post_pull',
1833
1813
            "Called after a pull operation completes. post_pull is called "
1834
1814
            "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 "
 
1815
            "bzr client.", (0, 15), None))
 
1816
        self.create_hook(HookPoint('pre_commit',
 
1817
            "Called after a commit is calculated but before it is is "
1838
1818
            "completed. pre_commit is called with (local, master, old_revno, "
1839
1819
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1840
1820
            "). old_revid is NULL_REVISION for the first commit to a branch, "
1842
1822
            "basis revision. hooks MUST NOT modify this delta. "
1843
1823
            " future_tree is an in-memory tree obtained from "
1844
1824
            "CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1845
 
            "tree.", (0,91))
1846
 
        self.add_hook('post_commit',
 
1825
            "tree.", (0,91), None))
 
1826
        self.create_hook(HookPoint('post_commit',
1847
1827
            "Called in the bzr client after a commit has completed. "
1848
1828
            "post_commit is called with (local, master, old_revno, old_revid, "
1849
1829
            "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',
 
1830
            "commit to a branch.", (0, 15), None))
 
1831
        self.create_hook(HookPoint('post_uncommit',
1852
1832
            "Called in the bzr client after an uncommit completes. "
1853
1833
            "post_uncommit is called with (local, master, old_revno, "
1854
1834
            "old_revid, new_revno, new_revid) where local is the local branch "
1855
1835
            "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',
 
1836
            "receives new_revno of 0, new_revid of None.", (0, 15), None))
 
1837
        self.create_hook(HookPoint('pre_change_branch_tip',
1858
1838
            "Called in bzr client and server before a change to the tip of a "
1859
1839
            "branch is made. pre_change_branch_tip is called with a "
1860
1840
            "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',
 
1841
            "commit, uncommit will all trigger this hook.", (1, 6), None))
 
1842
        self.create_hook(HookPoint('post_change_branch_tip',
1863
1843
            "Called in bzr client and server after a change to the tip of a "
1864
1844
            "branch is made. post_change_branch_tip is called with a "
1865
1845
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1866
 
            "commit, uncommit will all trigger this hook.", (1, 4))
1867
 
        self.add_hook('transform_fallback_location',
 
1846
            "commit, uncommit will all trigger this hook.", (1, 4), None))
 
1847
        self.create_hook(HookPoint('transform_fallback_location',
1868
1848
            "Called when a stacked branch is activating its fallback "
1869
1849
            "locations. transform_fallback_location is called with (branch, "
1870
1850
            "url), and should return a new url. Returning the same url "
1875
1855
            "fallback locations have not been activated. When there are "
1876
1856
            "multiple hooks installed for transform_fallback_location, "
1877
1857
            "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. "
 
1858
            "The order is however undefined.", (1, 9), None))
 
1859
        self.create_hook(HookPoint('automatic_tag_name',
 
1860
            "Called to determine an automatic tag name for a revision."
1881
1861
            "automatic_tag_name is called with (branch, revision_id) and "
1882
1862
            "should return a tag name or None if no tag name could be "
1883
1863
            "determined. The first non-None tag name returned will be used.",
1884
 
            (2, 2))
1885
 
        self.add_hook('post_branch_init',
 
1864
            (2, 2), None))
 
1865
        self.create_hook(HookPoint('post_branch_init',
1886
1866
            "Called after new branch initialization completes. "
1887
1867
            "post_branch_init is called with a "
1888
1868
            "bzrlib.branch.BranchInitHookParams. "
1889
1869
            "Note that init, branch and checkout (both heavyweight and "
1890
 
            "lightweight) will all trigger this hook.", (2, 2))
1891
 
        self.add_hook('post_switch',
 
1870
            "lightweight) will all trigger this hook.", (2, 2), None))
 
1871
        self.create_hook(HookPoint('post_switch',
1892
1872
            "Called after a checkout switches branch. "
1893
1873
            "post_switch is called with a "
1894
 
            "bzrlib.branch.SwitchHookParams.", (2, 2))
 
1874
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
1895
1875
 
1896
1876
 
1897
1877
 
1900
1880
 
1901
1881
 
1902
1882
class ChangeBranchTipParams(object):
1903
 
    """Object holding parameters passed to `*_change_branch_tip` hooks.
 
1883
    """Object holding parameters passed to *_change_branch_tip hooks.
1904
1884
 
1905
1885
    There are 5 fields that hooks may wish to access:
1906
1886
 
1938
1918
 
1939
1919
 
1940
1920
class BranchInitHookParams(object):
1941
 
    """Object holding parameters passed to `*_branch_init` hooks.
 
1921
    """Object holding parameters passed to *_branch_init hooks.
1942
1922
 
1943
1923
    There are 4 fields that hooks may wish to access:
1944
1924
 
1945
1925
    :ivar format: the branch format
1946
 
    :ivar bzrdir: the ControlDir where the branch will be/has been initialized
 
1926
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
1947
1927
    :ivar name: name of colocated branch, if any (or None)
1948
1928
    :ivar branch: the branch created
1949
1929
 
1952
1932
    branch, which refer to the original branch.
1953
1933
    """
1954
1934
 
1955
 
    def __init__(self, format, controldir, name, branch):
 
1935
    def __init__(self, format, a_bzrdir, name, branch):
1956
1936
        """Create a group of BranchInitHook parameters.
1957
1937
 
1958
1938
        :param format: the branch format
1959
 
        :param controldir: the ControlDir where the branch will be/has been
 
1939
        :param a_bzrdir: the BzrDir where the branch will be/has been
1960
1940
            initialized
1961
1941
        :param name: name of colocated branch, if any (or None)
1962
1942
        :param branch: the branch created
1966
1946
        in branch, which refer to the original branch.
1967
1947
        """
1968
1948
        self.format = format
1969
 
        self.bzrdir = controldir
 
1949
        self.bzrdir = a_bzrdir
1970
1950
        self.name = name
1971
1951
        self.branch = branch
1972
1952
 
1974
1954
        return self.__dict__ == other.__dict__
1975
1955
 
1976
1956
    def __repr__(self):
1977
 
        return "<%s of %s>" % (self.__class__.__name__, self.branch)
 
1957
        if self.branch:
 
1958
            return "<%s of %s>" % (self.__class__.__name__, self.branch)
 
1959
        else:
 
1960
            return "<%s of format:%s bzrdir:%s>" % (
 
1961
                self.__class__.__name__, self.branch,
 
1962
                self.format, self.bzrdir)
1978
1963
 
1979
1964
 
1980
1965
class SwitchHookParams(object):
1981
 
    """Object holding parameters passed to `*_switch` hooks.
 
1966
    """Object holding parameters passed to *_switch hooks.
1982
1967
 
1983
1968
    There are 4 fields that hooks may wish to access:
1984
1969
 
1985
 
    :ivar control_dir: ControlDir of the checkout to change
 
1970
    :ivar control_dir: BzrDir of the checkout to change
1986
1971
    :ivar to_branch: branch that the checkout is to reference
1987
1972
    :ivar force: skip the check for local commits in a heavy checkout
1988
1973
    :ivar revision_id: revision ID to switch to (or None)
1991
1976
    def __init__(self, control_dir, to_branch, force, revision_id):
1992
1977
        """Create a group of SwitchHook parameters.
1993
1978
 
1994
 
        :param control_dir: ControlDir of the checkout to change
 
1979
        :param control_dir: BzrDir of the checkout to change
1995
1980
        :param to_branch: branch that the checkout is to reference
1996
1981
        :param force: skip the check for local commits in a heavy checkout
1997
1982
        :param revision_id: revision ID to switch to (or None)
2010
1995
            self.revision_id)
2011
1996
 
2012
1997
 
 
1998
class BzrBranchFormat4(BranchFormat):
 
1999
    """Bzr branch format 4.
 
2000
 
 
2001
    This format has:
 
2002
     - a revision-history file.
 
2003
     - a branch-lock lock file [ to be shared with the bzrdir ]
 
2004
    """
 
2005
 
 
2006
    def get_format_description(self):
 
2007
        """See BranchFormat.get_format_description()."""
 
2008
        return "Branch format 4"
 
2009
 
 
2010
    def initialize(self, a_bzrdir, name=None):
 
2011
        """Create a branch of this format in a_bzrdir."""
 
2012
        utf8_files = [('revision-history', ''),
 
2013
                      ('branch-name', ''),
 
2014
                      ]
 
2015
        return self._initialize_helper(a_bzrdir, utf8_files, name=name,
 
2016
                                       lock_type='branch4', set_format=False)
 
2017
 
 
2018
    def __init__(self):
 
2019
        super(BzrBranchFormat4, self).__init__()
 
2020
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
 
2021
 
 
2022
    def network_name(self):
 
2023
        """The network name for this format is the control dirs disk label."""
 
2024
        return self._matchingbzrdir.get_format_string()
 
2025
 
 
2026
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
2027
        """See BranchFormat.open()."""
 
2028
        if not _found:
 
2029
            # we are being called directly and must probe.
 
2030
            raise NotImplementedError
 
2031
        return BzrBranch(_format=self,
 
2032
                         _control_files=a_bzrdir._control_files,
 
2033
                         a_bzrdir=a_bzrdir,
 
2034
                         name=name,
 
2035
                         _repository=a_bzrdir.open_repository())
 
2036
 
 
2037
    def __str__(self):
 
2038
        return "Bazaar-NG branch format 4"
 
2039
 
 
2040
 
2013
2041
class BranchFormatMetadir(BranchFormat):
2014
2042
    """Common logic for meta-dir based branch formats."""
2015
2043
 
2017
2045
        """What class to instantiate on open calls."""
2018
2046
        raise NotImplementedError(self._branch_class)
2019
2047
 
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
2048
    def network_name(self):
2058
2049
        """A simple byte string uniquely identifying this format for RPC calls.
2059
2050
 
2061
2052
        """
2062
2053
        return self.get_format_string()
2063
2054
 
2064
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2065
 
            found_repository=None):
 
2055
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2066
2056
        """See BranchFormat.open()."""
2067
2057
        if not _found:
2068
2058
            format = BranchFormat.find_format(a_bzrdir, name=name)
2073
2063
        try:
2074
2064
            control_files = lockable_files.LockableFiles(transport, 'lock',
2075
2065
                                                         lockdir.LockDir)
2076
 
            if found_repository is None:
2077
 
                found_repository = a_bzrdir.find_repository()
2078
2066
            return self._branch_class()(_format=self,
2079
2067
                              _control_files=control_files,
2080
2068
                              name=name,
2081
2069
                              a_bzrdir=a_bzrdir,
2082
 
                              _repository=found_repository,
 
2070
                              _repository=a_bzrdir.find_repository(),
2083
2071
                              ignore_fallbacks=ignore_fallbacks)
2084
2072
        except errors.NoSuchFile:
2085
2073
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2086
2074
 
2087
 
    @property
2088
 
    def _matchingbzrdir(self):
2089
 
        ret = bzrdir.BzrDirMetaFormat1()
2090
 
        ret.set_branch_format(self)
2091
 
        return ret
 
2075
    def __init__(self):
 
2076
        super(BranchFormatMetadir, self).__init__()
 
2077
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
2078
        self._matchingbzrdir.set_branch_format(self)
2092
2079
 
2093
2080
    def supports_tags(self):
2094
2081
        return True
2095
2082
 
2096
 
    def supports_leaving_lock(self):
2097
 
        return True
2098
 
 
2099
2083
 
2100
2084
class BzrBranchFormat5(BranchFormatMetadir):
2101
2085
    """Bzr branch format 5.
2121
2105
        """See BranchFormat.get_format_description()."""
2122
2106
        return "Branch format 5"
2123
2107
 
2124
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2125
 
                   append_revisions_only=None):
 
2108
    def initialize(self, a_bzrdir, name=None):
2126
2109
        """Create a branch of this format in a_bzrdir."""
2127
 
        if append_revisions_only:
2128
 
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2129
2110
        utf8_files = [('revision-history', ''),
2130
2111
                      ('branch-name', ''),
2131
2112
                      ]
2132
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2113
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2133
2114
 
2134
2115
    def supports_tags(self):
2135
2116
        return False
2157
2138
        """See BranchFormat.get_format_description()."""
2158
2139
        return "Branch format 6"
2159
2140
 
2160
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2161
 
                   append_revisions_only=None):
 
2141
    def initialize(self, a_bzrdir, name=None):
2162
2142
        """Create a branch of this format in a_bzrdir."""
2163
2143
        utf8_files = [('last-revision', '0 null:\n'),
2164
 
                      ('branch.conf',
2165
 
                          self._get_initial_config(append_revisions_only)),
 
2144
                      ('branch.conf', ''),
2166
2145
                      ('tags', ''),
2167
2146
                      ]
2168
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2147
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2169
2148
 
2170
2149
    def make_tags(self, branch):
2171
2150
        """See bzrlib.branch.BranchFormat.make_tags()."""
2172
 
        return _mod_tag.BasicTags(branch)
 
2151
        return BasicTags(branch)
2173
2152
 
2174
2153
    def supports_set_append_revisions_only(self):
2175
2154
        return True
2189
2168
        """See BranchFormat.get_format_description()."""
2190
2169
        return "Branch format 8"
2191
2170
 
2192
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2193
 
                   append_revisions_only=None):
 
2171
    def initialize(self, a_bzrdir, name=None):
2194
2172
        """Create a branch of this format in a_bzrdir."""
2195
2173
        utf8_files = [('last-revision', '0 null:\n'),
2196
 
                      ('branch.conf',
2197
 
                          self._get_initial_config(append_revisions_only)),
 
2174
                      ('branch.conf', ''),
2198
2175
                      ('tags', ''),
2199
2176
                      ('references', '')
2200
2177
                      ]
2201
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2178
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2179
 
 
2180
    def __init__(self):
 
2181
        super(BzrBranchFormat8, self).__init__()
 
2182
        self._matchingbzrdir.repository_format = \
 
2183
            RepositoryFormatKnitPack5RichRoot()
2202
2184
 
2203
2185
    def make_tags(self, branch):
2204
2186
        """See bzrlib.branch.BranchFormat.make_tags()."""
2205
 
        return _mod_tag.BasicTags(branch)
 
2187
        return BasicTags(branch)
2206
2188
 
2207
2189
    def supports_set_append_revisions_only(self):
2208
2190
        return True
2213
2195
    supports_reference_locations = True
2214
2196
 
2215
2197
 
2216
 
class BzrBranchFormat7(BranchFormatMetadir):
 
2198
class BzrBranchFormat7(BzrBranchFormat8):
2217
2199
    """Branch format with last-revision, tags, and a stacked location pointer.
2218
2200
 
2219
2201
    The stacked location pointer is passed down to the repository and requires
2222
2204
    This format was introduced in bzr 1.6.
2223
2205
    """
2224
2206
 
2225
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2226
 
                   append_revisions_only=None):
 
2207
    def initialize(self, a_bzrdir, name=None):
2227
2208
        """Create a branch of this format in a_bzrdir."""
2228
2209
        utf8_files = [('last-revision', '0 null:\n'),
2229
 
                      ('branch.conf',
2230
 
                          self._get_initial_config(append_revisions_only)),
 
2210
                      ('branch.conf', ''),
2231
2211
                      ('tags', ''),
2232
2212
                      ]
2233
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2213
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2234
2214
 
2235
2215
    def _branch_class(self):
2236
2216
        return BzrBranch7
2246
2226
    def supports_set_append_revisions_only(self):
2247
2227
        return True
2248
2228
 
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
2229
    supports_reference_locations = False
2257
2230
 
2258
2231
 
2259
 
class BranchReferenceFormat(BranchFormatMetadir):
 
2232
class BranchReferenceFormat(BranchFormat):
2260
2233
    """Bzr branch reference format.
2261
2234
 
2262
2235
    Branch references are used in implementing checkouts, they
2285
2258
        transport = a_bzrdir.get_branch_transport(None, name=name)
2286
2259
        location = transport.put_bytes('location', to_branch.base)
2287
2260
 
2288
 
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2289
 
            repository=None, append_revisions_only=None):
 
2261
    def initialize(self, a_bzrdir, name=None, target_branch=None):
2290
2262
        """Create a branch of this format in a_bzrdir."""
2291
2263
        if target_branch is None:
2292
2264
            # this format does not implement branch itself, thus the implicit
2293
2265
            # creation contract must see it as uninitializable
2294
2266
            raise errors.UninitializableFormat(self)
2295
2267
        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
2268
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2299
2269
        branch_transport.put_bytes('location',
2300
2270
            target_branch.bzrdir.user_url)
2305
2275
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2306
2276
        return branch
2307
2277
 
 
2278
    def __init__(self):
 
2279
        super(BranchReferenceFormat, self).__init__()
 
2280
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
2281
        self._matchingbzrdir.set_branch_format(self)
 
2282
 
2308
2283
    def _make_reference_clone_function(format, a_branch):
2309
2284
        """Create a clone() routine for a branch dynamically."""
2310
2285
        def clone(to_bzrdir, revision_id=None,
2317
2292
        return clone
2318
2293
 
2319
2294
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2320
 
             possible_transports=None, ignore_fallbacks=False,
2321
 
             found_repository=None):
 
2295
             possible_transports=None, ignore_fallbacks=False):
2322
2296
        """Return the branch that the branch reference in a_bzrdir points at.
2323
2297
 
2324
2298
        :param a_bzrdir: A BzrDir that contains a branch.
2339
2313
                    (format, self))
2340
2314
        if location is None:
2341
2315
            location = self.get_reference(a_bzrdir, name)
2342
 
        real_bzrdir = controldir.ControlDir.open(
 
2316
        real_bzrdir = bzrdir.BzrDir.open(
2343
2317
            location, possible_transports=possible_transports)
2344
2318
        result = real_bzrdir.open_branch(name=name, 
2345
2319
            ignore_fallbacks=ignore_fallbacks)
2355
2329
        return result
2356
2330
 
2357
2331
 
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
2332
network_format_registry = registry.FormatRegistry()
2373
2333
"""Registry of formats indexed by their network name.
2374
2334
 
2377
2337
BranchFormat.network_name() for more detail.
2378
2338
"""
2379
2339
 
2380
 
format_registry = BranchFormatRegistry(network_format_registry)
2381
 
 
2382
2340
 
2383
2341
# formats which have no format string are not discoverable
2384
2342
# and not independently creatable, so are not registered.
2386
2344
__format6 = BzrBranchFormat6()
2387
2345
__format7 = BzrBranchFormat7()
2388
2346
__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)
 
2347
BranchFormat.register_format(__format5)
 
2348
BranchFormat.register_format(BranchReferenceFormat())
 
2349
BranchFormat.register_format(__format6)
 
2350
BranchFormat.register_format(__format7)
 
2351
BranchFormat.register_format(__format8)
 
2352
BranchFormat.set_default_format(__format7)
 
2353
_legacy_formats = [BzrBranchFormat4(),
 
2354
    ]
 
2355
network_format_registry.register(
 
2356
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2395
2357
 
2396
2358
 
2397
2359
class BranchWriteLockResult(LogicalLockResult):
2464
2426
    base = property(_get_base, doc="The URL for the root of this branch.")
2465
2427
 
2466
2428
    def _get_config(self):
2467
 
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
 
2429
        return TransportConfig(self._transport, 'branch.conf')
2468
2430
 
2469
2431
    def is_locked(self):
2470
2432
        return self.control_files.is_locked()
2545
2507
        """See Branch.print_file."""
2546
2508
        return self.repository.print_file(file, revision_id)
2547
2509
 
 
2510
    def _write_revision_history(self, history):
 
2511
        """Factored out of set_revision_history.
 
2512
 
 
2513
        This performs the actual writing to disk.
 
2514
        It is intended to be called by BzrBranch5.set_revision_history."""
 
2515
        self._transport.put_bytes(
 
2516
            'revision-history', '\n'.join(history),
 
2517
            mode=self.bzrdir._get_file_mode())
 
2518
 
 
2519
    @needs_write_lock
 
2520
    def set_revision_history(self, rev_history):
 
2521
        """See Branch.set_revision_history."""
 
2522
        if 'evil' in debug.debug_flags:
 
2523
            mutter_callsite(3, "set_revision_history scales with history.")
 
2524
        check_not_reserved_id = _mod_revision.check_not_reserved_id
 
2525
        for rev_id in rev_history:
 
2526
            check_not_reserved_id(rev_id)
 
2527
        if Branch.hooks['post_change_branch_tip']:
 
2528
            # Don't calculate the last_revision_info() if there are no hooks
 
2529
            # that will use it.
 
2530
            old_revno, old_revid = self.last_revision_info()
 
2531
        if len(rev_history) == 0:
 
2532
            revid = _mod_revision.NULL_REVISION
 
2533
        else:
 
2534
            revid = rev_history[-1]
 
2535
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
 
2536
        self._write_revision_history(rev_history)
 
2537
        self._clear_cached_state()
 
2538
        self._cache_revision_history(rev_history)
 
2539
        for hook in Branch.hooks['set_rh']:
 
2540
            hook(self, rev_history)
 
2541
        if Branch.hooks['post_change_branch_tip']:
 
2542
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2543
 
 
2544
    def _synchronize_history(self, destination, revision_id):
 
2545
        """Synchronize last revision and revision history between branches.
 
2546
 
 
2547
        This version is most efficient when the destination is also a
 
2548
        BzrBranch5, but works for BzrBranch6 as long as the revision
 
2549
        history is the true lefthand parent history, and all of the revisions
 
2550
        are in the destination's repository.  If not, set_revision_history
 
2551
        will fail.
 
2552
 
 
2553
        :param destination: The branch to copy the history into
 
2554
        :param revision_id: The revision-id to truncate history at.  May
 
2555
          be None to copy complete history.
 
2556
        """
 
2557
        if not isinstance(destination._format, BzrBranchFormat5):
 
2558
            super(BzrBranch, self)._synchronize_history(
 
2559
                destination, revision_id)
 
2560
            return
 
2561
        if revision_id == _mod_revision.NULL_REVISION:
 
2562
            new_history = []
 
2563
        else:
 
2564
            new_history = self.revision_history()
 
2565
        if revision_id is not None and new_history != []:
 
2566
            try:
 
2567
                new_history = new_history[:new_history.index(revision_id) + 1]
 
2568
            except ValueError:
 
2569
                rev = self.repository.get_revision(revision_id)
 
2570
                new_history = rev.get_history(self.repository)[1:]
 
2571
        destination.set_revision_history(new_history)
 
2572
 
2548
2573
    @needs_write_lock
2549
2574
    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)
 
2575
        """Set the last revision of this branch.
 
2576
 
 
2577
        The caller is responsible for checking that the revno is correct
 
2578
        for this revision id.
 
2579
 
 
2580
        It may be possible to set the branch last revision to an id not
 
2581
        present in the repository.  However, branches can also be
 
2582
        configured to check constraints on history, in which case this may not
 
2583
        be permitted.
 
2584
        """
2552
2585
        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)
 
2586
        # this old format stores the full history, but this api doesn't
 
2587
        # provide it, so we must generate, and might as well check it's
 
2588
        # correct
 
2589
        history = self._lefthand_history(revision_id)
 
2590
        if len(history) != revno:
 
2591
            raise AssertionError('%d != %d' % (len(history), revno))
 
2592
        self.set_revision_history(history)
 
2593
 
 
2594
    def _gen_revision_history(self):
 
2595
        history = self._transport.get_bytes('revision-history').split('\n')
 
2596
        if history[-1:] == ['']:
 
2597
            # There shouldn't be a trailing newline, but just in case.
 
2598
            history.pop()
 
2599
        return history
 
2600
 
 
2601
    @needs_write_lock
 
2602
    def generate_revision_history(self, revision_id, last_rev=None,
 
2603
        other_branch=None):
 
2604
        """Create a new revision history that will finish with revision_id.
 
2605
 
 
2606
        :param revision_id: the new tip to use.
 
2607
        :param last_rev: The previous last_revision. If not None, then this
 
2608
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
2609
        :param other_branch: The other branch that DivergedBranches should
 
2610
            raise with respect to.
 
2611
        """
 
2612
        self.set_revision_history(self._lefthand_history(revision_id,
 
2613
            last_rev, other_branch))
2561
2614
 
2562
2615
    def basis_tree(self):
2563
2616
        """See Branch.basis_tree."""
2572
2625
                pass
2573
2626
        return None
2574
2627
 
 
2628
    def _basic_push(self, target, overwrite, stop_revision):
 
2629
        """Basic implementation of push without bound branches or hooks.
 
2630
 
 
2631
        Must be called with source read locked and target write locked.
 
2632
        """
 
2633
        result = BranchPushResult()
 
2634
        result.source_branch = self
 
2635
        result.target_branch = target
 
2636
        result.old_revno, result.old_revid = target.last_revision_info()
 
2637
        self.update_references(target)
 
2638
        if result.old_revid != self.last_revision():
 
2639
            # We assume that during 'push' this repository is closer than
 
2640
            # the target.
 
2641
            graph = self.repository.get_graph(target.repository)
 
2642
            target.update_revisions(self, stop_revision,
 
2643
                overwrite=overwrite, graph=graph)
 
2644
        if self._push_should_merge_tags():
 
2645
            result.tag_conflicts = self.tags.merge_to(target.tags,
 
2646
                overwrite)
 
2647
        result.new_revno, result.new_revid = target.last_revision_info()
 
2648
        return result
 
2649
 
2575
2650
    def get_stacked_on_url(self):
2576
2651
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2577
2652
 
2588
2663
            self._transport.put_bytes('parent', url + '\n',
2589
2664
                mode=self.bzrdir._get_file_mode())
2590
2665
 
 
2666
 
 
2667
class BzrBranch5(BzrBranch):
 
2668
    """A format 5 branch. This supports new features over plain branches.
 
2669
 
 
2670
    It has support for a master_branch which is the data for bound branches.
 
2671
    """
 
2672
 
 
2673
    def get_bound_location(self):
 
2674
        try:
 
2675
            return self._transport.get_bytes('bound')[:-1]
 
2676
        except errors.NoSuchFile:
 
2677
            return None
 
2678
 
 
2679
    @needs_read_lock
 
2680
    def get_master_branch(self, possible_transports=None):
 
2681
        """Return the branch we are bound to.
 
2682
 
 
2683
        :return: Either a Branch, or None
 
2684
 
 
2685
        This could memoise the branch, but if thats done
 
2686
        it must be revalidated on each new lock.
 
2687
        So for now we just don't memoise it.
 
2688
        # RBC 20060304 review this decision.
 
2689
        """
 
2690
        bound_loc = self.get_bound_location()
 
2691
        if not bound_loc:
 
2692
            return None
 
2693
        try:
 
2694
            return Branch.open(bound_loc,
 
2695
                               possible_transports=possible_transports)
 
2696
        except (errors.NotBranchError, errors.ConnectionError), e:
 
2697
            raise errors.BoundBranchConnectionFailure(
 
2698
                    self, bound_loc, e)
 
2699
 
2591
2700
    @needs_write_lock
2592
 
    def unbind(self):
2593
 
        """If bound, unbind"""
2594
 
        return self.set_bound_location(None)
 
2701
    def set_bound_location(self, location):
 
2702
        """Set the target where this branch is bound to.
 
2703
 
 
2704
        :param location: URL to the target branch
 
2705
        """
 
2706
        if location:
 
2707
            self._transport.put_bytes('bound', location+'\n',
 
2708
                mode=self.bzrdir._get_file_mode())
 
2709
        else:
 
2710
            try:
 
2711
                self._transport.delete('bound')
 
2712
            except errors.NoSuchFile:
 
2713
                return False
 
2714
            return True
2595
2715
 
2596
2716
    @needs_write_lock
2597
2717
    def bind(self, other):
2619
2739
        # history around
2620
2740
        self.set_bound_location(other.base)
2621
2741
 
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
2742
    @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
 
2743
    def unbind(self):
 
2744
        """If bound, unbind"""
 
2745
        return self.set_bound_location(None)
2666
2746
 
2667
2747
    @needs_write_lock
2668
2748
    def update(self, possible_transports=None):
2681
2761
            return old_tip
2682
2762
        return None
2683
2763
 
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):
 
2764
 
 
2765
class BzrBranch8(BzrBranch5):
2813
2766
    """A branch that stores tree-reference locations."""
2814
2767
 
2815
2768
    def _open_hook(self):
2841
2794
        self._last_revision_info_cache = None
2842
2795
        self._reference_info = None
2843
2796
 
 
2797
    def _last_revision_info(self):
 
2798
        revision_string = self._transport.get_bytes('last-revision')
 
2799
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
 
2800
        revision_id = cache_utf8.get_cached_utf8(revision_id)
 
2801
        revno = int(revno)
 
2802
        return revno, revision_id
 
2803
 
 
2804
    def _write_last_revision_info(self, revno, revision_id):
 
2805
        """Simply write out the revision id, with no checks.
 
2806
 
 
2807
        Use set_last_revision_info to perform this safely.
 
2808
 
 
2809
        Does not update the revision_history cache.
 
2810
        Intended to be called by set_last_revision_info and
 
2811
        _write_revision_history.
 
2812
        """
 
2813
        revision_id = _mod_revision.ensure_null(revision_id)
 
2814
        out_string = '%d %s\n' % (revno, revision_id)
 
2815
        self._transport.put_bytes('last-revision', out_string,
 
2816
            mode=self.bzrdir._get_file_mode())
 
2817
 
 
2818
    @needs_write_lock
 
2819
    def set_last_revision_info(self, revno, revision_id):
 
2820
        revision_id = _mod_revision.ensure_null(revision_id)
 
2821
        old_revno, old_revid = self.last_revision_info()
 
2822
        if self._get_append_revisions_only():
 
2823
            self._check_history_violation(revision_id)
 
2824
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
 
2825
        self._write_last_revision_info(revno, revision_id)
 
2826
        self._clear_cached_state()
 
2827
        self._last_revision_info_cache = revno, revision_id
 
2828
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2829
 
 
2830
    def _synchronize_history(self, destination, revision_id):
 
2831
        """Synchronize last revision and revision history between branches.
 
2832
 
 
2833
        :see: Branch._synchronize_history
 
2834
        """
 
2835
        # XXX: The base Branch has a fast implementation of this method based
 
2836
        # on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
 
2837
        # that uses set_revision_history.  This class inherits from BzrBranch5,
 
2838
        # but wants the fast implementation, so it calls
 
2839
        # Branch._synchronize_history directly.
 
2840
        Branch._synchronize_history(self, destination, revision_id)
 
2841
 
2844
2842
    def _check_history_violation(self, revision_id):
2845
 
        current_revid = self.last_revision()
2846
 
        last_revision = _mod_revision.ensure_null(current_revid)
 
2843
        last_revision = _mod_revision.ensure_null(self.last_revision())
2847
2844
        if _mod_revision.is_null(last_revision):
2848
2845
            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)
 
2846
        if last_revision not in self._lefthand_history(revision_id):
 
2847
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
2854
2848
 
2855
2849
    def _gen_revision_history(self):
2856
2850
        """Generate the revision history from last revision
2859
2853
        self._extend_partial_history(stop_index=last_revno-1)
2860
2854
        return list(reversed(self._partial_revision_history_cache))
2861
2855
 
 
2856
    def _write_revision_history(self, history):
 
2857
        """Factored out of set_revision_history.
 
2858
 
 
2859
        This performs the actual writing to disk, with format-specific checks.
 
2860
        It is intended to be called by BzrBranch5.set_revision_history.
 
2861
        """
 
2862
        if len(history) == 0:
 
2863
            last_revision = 'null:'
 
2864
        else:
 
2865
            if history != self._lefthand_history(history[-1]):
 
2866
                raise errors.NotLefthandHistory(history)
 
2867
            last_revision = history[-1]
 
2868
        if self._get_append_revisions_only():
 
2869
            self._check_history_violation(last_revision)
 
2870
        self._write_last_revision_info(len(history), last_revision)
 
2871
 
2862
2872
    @needs_write_lock
2863
2873
    def _set_parent_location(self, url):
2864
2874
        """Set the parent branch"""
2950
2960
 
2951
2961
    def set_bound_location(self, location):
2952
2962
        """See Branch.set_push_location."""
2953
 
        self._master_branch_cache = None
2954
2963
        result = None
2955
2964
        config = self.get_config()
2956
2965
        if location is None:
2987
2996
        # you can always ask for the URL; but you might not be able to use it
2988
2997
        # if the repo can't support stacking.
2989
2998
        ## 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)
 
2999
        stacked_url = self._get_config_location('stacked_on_location')
2995
3000
        if stacked_url is None:
2996
3001
            raise errors.NotStacked(self)
2997
3002
        return stacked_url
2998
3003
 
 
3004
    def _get_append_revisions_only(self):
 
3005
        return self.get_config(
 
3006
            ).get_user_option_as_bool('append_revisions_only')
 
3007
 
 
3008
    @needs_write_lock
 
3009
    def generate_revision_history(self, revision_id, last_rev=None,
 
3010
                                  other_branch=None):
 
3011
        """See BzrBranch5.generate_revision_history"""
 
3012
        history = self._lefthand_history(revision_id, last_rev, other_branch)
 
3013
        revno = len(history)
 
3014
        self.set_last_revision_info(revno, revision_id)
 
3015
 
2999
3016
    @needs_read_lock
3000
3017
    def get_rev_id(self, revno, history=None):
3001
3018
        """Find the revision id of the specified revno."""
3025
3042
        try:
3026
3043
            index = self._partial_revision_history_cache.index(revision_id)
3027
3044
        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)
 
3045
            self._extend_partial_history(stop_revision=revision_id)
3032
3046
            index = len(self._partial_revision_history_cache) - 1
3033
3047
            if self._partial_revision_history_cache[index] != revision_id:
3034
3048
                raise errors.NoSuchRevision(self, revision_id)
3087
3101
    :ivar local_branch: target branch if there is a Master, else None
3088
3102
    :ivar target_branch: Target/destination branch object. (write locked)
3089
3103
    :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
3104
    """
3092
3105
 
3093
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3094
3106
    def __int__(self):
3095
 
        """Return the relative change in revno.
3096
 
 
3097
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3098
 
        """
 
3107
        # DEPRECATED: pull used to return the change in revno
3099
3108
        return self.new_revno - self.old_revno
3100
3109
 
3101
3110
    def report(self, to_file):
3102
 
        tag_conflicts = getattr(self, "tag_conflicts", None)
3103
 
        tag_updates = getattr(self, "tag_updates", None)
3104
3111
        if not is_quiet():
3105
 
            if self.old_revid != self.new_revid:
 
3112
            if self.old_revid == self.new_revid:
 
3113
                to_file.write('No revisions to pull.\n')
 
3114
            else:
3106
3115
                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
3116
        self._show_tag_conficts(to_file)
3115
3117
 
3116
3118
 
3133
3135
        target, otherwise it will be None.
3134
3136
    """
3135
3137
 
3136
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3137
3138
    def __int__(self):
3138
 
        """Return the relative change in revno.
3139
 
 
3140
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3141
 
        """
 
3139
        # DEPRECATED: push used to return the change in revno
3142
3140
        return self.new_revno - self.old_revno
3143
3141
 
3144
3142
    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.'))
 
3143
        """Write a human-readable description of the result."""
 
3144
        if self.old_revid == self.new_revid:
 
3145
            note('No new revisions to push.')
 
3146
        else:
 
3147
            note('Pushed up to revision %d.' % self.new_revno)
3161
3148
        self._show_tag_conficts(to_file)
3162
3149
 
3163
3150
 
3177
3164
        :param verbose: Requests more detailed display of what was checked,
3178
3165
            if any.
3179
3166
        """
3180
 
        note(gettext('checked branch {0} format {1}').format(
3181
 
                                self.branch.user_url, self.branch._format))
 
3167
        note('checked branch %s format %s', self.branch.user_url,
 
3168
            self.branch._format)
3182
3169
        for error in self.errors:
3183
 
            note(gettext('found error:%s'), error)
 
3170
            note('found error:%s', error)
3184
3171
 
3185
3172
 
3186
3173
class Converter5to6(object):
3225
3212
 
3226
3213
 
3227
3214
class Converter7to8(object):
3228
 
    """Perform an in-place upgrade of format 7 to format 8"""
 
3215
    """Perform an in-place upgrade of format 6 to format 7"""
3229
3216
 
3230
3217
    def convert(self, branch):
3231
3218
        format = BzrBranchFormat8()
3234
3221
        branch._transport.put_bytes('format', format.get_format_string())
3235
3222
 
3236
3223
 
 
3224
def _run_with_write_locked_target(target, callable, *args, **kwargs):
 
3225
    """Run ``callable(*args, **kwargs)``, write-locking target for the
 
3226
    duration.
 
3227
 
 
3228
    _run_with_write_locked_target will attempt to release the lock it acquires.
 
3229
 
 
3230
    If an exception is raised by callable, then that exception *will* be
 
3231
    propagated, even if the unlock attempt raises its own error.  Thus
 
3232
    _run_with_write_locked_target should be preferred to simply doing::
 
3233
 
 
3234
        target.lock_write()
 
3235
        try:
 
3236
            return callable(*args, **kwargs)
 
3237
        finally:
 
3238
            target.unlock()
 
3239
 
 
3240
    """
 
3241
    # This is very similar to bzrlib.decorators.needs_write_lock.  Perhaps they
 
3242
    # should share code?
 
3243
    target.lock_write()
 
3244
    try:
 
3245
        result = callable(*args, **kwargs)
 
3246
    except:
 
3247
        exc_info = sys.exc_info()
 
3248
        try:
 
3249
            target.unlock()
 
3250
        finally:
 
3251
            raise exc_info[0], exc_info[1], exc_info[2]
 
3252
    else:
 
3253
        target.unlock()
 
3254
        return result
 
3255
 
 
3256
 
3237
3257
class InterBranch(InterObject):
3238
3258
    """This class represents operations taking place between two branches.
3239
3259
 
3267
3287
        raise NotImplementedError(self.pull)
3268
3288
 
3269
3289
    @needs_write_lock
3270
 
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
3290
    def update_revisions(self, stop_revision=None, overwrite=False,
 
3291
                         graph=None):
 
3292
        """Pull in new perfect-fit revisions.
 
3293
 
 
3294
        :param stop_revision: Updated until the given revision
 
3295
        :param overwrite: Always set the branch pointer, rather than checking
 
3296
            to see if it is a proper descendant.
 
3297
        :param graph: A Graph object that can be used to query history
 
3298
            information. This can be None.
 
3299
        :return: None
 
3300
        """
 
3301
        raise NotImplementedError(self.update_revisions)
 
3302
 
 
3303
    @needs_write_lock
 
3304
    def push(self, overwrite=False, stop_revision=None,
3271
3305
             _override_hook_source_branch=None):
3272
3306
        """Mirror the source branch into the target branch.
3273
3307
 
3284
3318
        """
3285
3319
        raise NotImplementedError(self.copy_content_into)
3286
3320
 
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
3321
 
3297
3322
class GenericInterBranch(InterBranch):
3298
3323
    """InterBranch implementation that uses public Branch functions."""
3304
3329
 
3305
3330
    @classmethod
3306
3331
    def _get_branch_formats_to_test(klass):
3307
 
        return [(format_registry.get_default(), format_registry.get_default())]
 
3332
        return [(BranchFormat._default_format, BranchFormat._default_format)]
3308
3333
 
3309
3334
    @classmethod
3310
3335
    def unwrap_format(klass, format):
3311
3336
        if isinstance(format, remote.RemoteBranchFormat):
3312
3337
            format._ensure_real()
3313
3338
            return format._custom_format
3314
 
        return format
 
3339
        return format                                                                                                  
3315
3340
 
3316
3341
    @needs_write_lock
3317
3342
    def copy_content_into(self, revision_id=None):
3333
3358
            self.source.tags.merge_to(self.target.tags)
3334
3359
 
3335
3360
    @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):
 
3361
    def update_revisions(self, stop_revision=None, overwrite=False,
 
3362
        graph=None):
 
3363
        """See InterBranch.update_revisions()."""
3357
3364
        other_revno, other_last_revision = self.source.last_revision_info()
3358
3365
        stop_revno = None # unknown
3359
3366
        if stop_revision is None:
3370
3377
        # case of having something to pull, and so that the check for
3371
3378
        # already merged can operate on the just fetched graph, which will
3372
3379
        # be cached in memory.
3373
 
        self.fetch(stop_revision=stop_revision)
 
3380
        self.target.fetch(self.source, stop_revision)
3374
3381
        # Check to see if one is an ancestor of the other
3375
3382
        if not overwrite:
3376
3383
            if graph is None:
3404
3411
        if local and not bound_location:
3405
3412
            raise errors.LocalRequiresBoundBranch()
3406
3413
        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:
 
3414
        if not local and bound_location and self.source.user_url != bound_location:
3418
3415
            # not pulling from master, so we need to update master.
3419
3416
            master_branch = self.target.get_master_branch(possible_transports)
3420
3417
            master_branch.lock_write()
3426
3423
            return self._pull(overwrite,
3427
3424
                stop_revision, _hook_master=master_branch,
3428
3425
                run_hooks=run_hooks,
3429
 
                _override_hook_target=_override_hook_target,
3430
 
                merge_tags_to_master=not source_is_master)
 
3426
                _override_hook_target=_override_hook_target)
3431
3427
        finally:
3432
3428
            if master_branch:
3433
3429
                master_branch.unlock()
3434
3430
 
3435
 
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
3431
    def push(self, overwrite=False, stop_revision=None,
3436
3432
             _override_hook_source_branch=None):
3437
3433
        """See InterBranch.push.
3438
3434
 
3439
3435
        This is the basic concrete implementation of push()
3440
3436
 
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.
 
3437
        :param _override_hook_source_branch: If specified, run
 
3438
        the hooks passing this Branch as the source, rather than self.
 
3439
        This is for use of RemoteBranch, where push is delegated to the
 
3440
        underlying vfs-based Branch.
3445
3441
        """
3446
 
        if lossy:
3447
 
            raise errors.LossyPushToSameVCS(self.source, self.target)
3448
3442
        # TODO: Public option to disable running hooks - should be trivial but
3449
3443
        # 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,
 
3444
        self.source.lock_read()
 
3445
        try:
 
3446
            return _run_with_write_locked_target(
 
3447
                self.target, self._push_with_bound_branches, overwrite,
 
3448
                stop_revision,
 
3449
                _override_hook_source_branch=_override_hook_source_branch)
 
3450
        finally:
 
3451
            self.source.unlock()
 
3452
 
 
3453
    def _push_with_bound_branches(self, overwrite, stop_revision,
3480
3454
            _override_hook_source_branch=None):
3481
3455
        """Push from source into target, and into target's master if any.
3482
3456
        """
3494
3468
            # be bound to itself? -- mbp 20070507
3495
3469
            master_branch = self.target.get_master_branch()
3496
3470
            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
 
3471
            try:
 
3472
                # push into the master from the source branch.
 
3473
                self.source._basic_push(master_branch, overwrite, stop_revision)
 
3474
                # and push into the target branch from the source. Note that we
 
3475
                # push from the source branch again, because its considered the
 
3476
                # highest bandwidth repository.
 
3477
                result = self.source._basic_push(self.target, overwrite,
 
3478
                    stop_revision)
 
3479
                result.master_branch = master_branch
 
3480
                result.local_branch = self.target
 
3481
                _run_hooks()
 
3482
                return result
 
3483
            finally:
 
3484
                master_branch.unlock()
3507
3485
        else:
3508
 
            master_branch = None
3509
3486
            # no master branch
3510
 
            result = self._basic_push(overwrite, stop_revision)
 
3487
            result = self.source._basic_push(self.target, overwrite,
 
3488
                stop_revision)
3511
3489
            # TODO: Why set master_branch and local_branch if there's no
3512
3490
            # binding?  Maybe cleaner to just leave them unset? -- mbp
3513
3491
            # 20070504
3514
3492
            result.master_branch = self.target
3515
3493
            result.local_branch = None
3516
 
        _run_hooks()
3517
 
        return result
 
3494
            _run_hooks()
 
3495
            return result
3518
3496
 
3519
3497
    def _pull(self, overwrite=False, stop_revision=None,
3520
3498
             possible_transports=None, _hook_master=None, run_hooks=True,
3521
 
             _override_hook_target=None, local=False,
3522
 
             merge_tags_to_master=True):
 
3499
             _override_hook_target=None, local=False):
3523
3500
        """See Branch.pull.
3524
3501
 
3525
3502
        This function is the core worker, used by GenericInterBranch.pull to
3530
3507
        :param run_hooks: Private parameter - if false, this branch
3531
3508
            is being called because it's the master of the primary branch,
3532
3509
            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
3510
        :param _override_hook_target: Private parameter - set the branch to be
3536
3511
            supplied as the target_branch to pull hooks.
3537
3512
        :param local: Only update the local branch, and not the bound branch.
3556
3531
            # -- JRV20090506
3557
3532
            result.old_revno, result.old_revid = \
3558
3533
                self.target.last_revision_info()
3559
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3560
 
                graph=graph)
 
3534
            self.target.update_revisions(self.source, stop_revision,
 
3535
                overwrite=overwrite, graph=graph)
3561
3536
            # TODO: The old revid should be specified when merging tags, 
3562
3537
            # so a tags implementation that versions tags can only 
3563
3538
            # 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))
 
3539
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3540
                overwrite)
3567
3541
            result.new_revno, result.new_revid = self.target.last_revision_info()
3568
3542
            if _hook_master:
3569
3543
                result.master_branch = _hook_master