~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: 2011-05-18 13:02:52 UTC
  • mfrom: (5830.3.6 i18n-msgfmt)
  • Revision ID: pqm@pqm.ubuntu.com-20110518130252-ky96qcvzt6o0zg3f
(mbp) add build_mo command to setup.py (INADA Naoki)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2012 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
from __future__ import absolute_import
18
 
 
19
 
import bzrlib.bzrdir
20
17
 
21
18
from cStringIO import StringIO
 
19
import sys
22
20
 
23
21
from bzrlib.lazy_import import lazy_import
24
22
lazy_import(globals(), """
25
 
import itertools
 
23
from itertools import chain
26
24
from bzrlib import (
27
 
    bzrdir,
28
 
    controldir,
29
 
    cache_utf8,
30
 
    cleanup,
31
 
    config as _mod_config,
32
 
    debug,
33
 
    errors,
34
 
    fetch,
35
 
    graph as _mod_graph,
36
 
    lockdir,
37
 
    lockable_files,
38
 
    remote,
39
 
    repository,
40
 
    revision as _mod_revision,
41
 
    rio,
42
 
    shelf,
43
 
    tag as _mod_tag,
44
 
    transport,
45
 
    ui,
46
 
    urlutils,
47
 
    vf_search,
 
25
        bzrdir,
 
26
        cache_utf8,
 
27
        config as _mod_config,
 
28
        debug,
 
29
        errors,
 
30
        fetch,
 
31
        graph as _mod_graph,
 
32
        lockdir,
 
33
        lockable_files,
 
34
        remote,
 
35
        repository,
 
36
        revision as _mod_revision,
 
37
        rio,
 
38
        transport,
 
39
        ui,
 
40
        urlutils,
 
41
        )
 
42
from bzrlib.config import BranchConfig, TransportConfig
 
43
from bzrlib.tag import (
 
44
    BasicTags,
 
45
    DisabledTags,
48
46
    )
49
 
from bzrlib.i18n import gettext, ngettext
50
47
""")
51
48
 
52
 
# Explicitly import bzrlib.bzrdir so that the BzrProber
53
 
# is guaranteed to be registered.
54
 
import bzrlib.bzrdir
55
 
 
56
49
from bzrlib import (
57
 
    bzrdir,
58
50
    controldir,
59
51
    )
60
52
from bzrlib.decorators import (
73
65
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
74
66
 
75
67
 
 
68
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
 
69
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
 
70
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
 
71
 
 
72
 
76
73
class Branch(controldir.ControlComponent):
77
74
    """Branch holding a history of revisions.
78
75
 
95
92
    def user_transport(self):
96
93
        return self.bzrdir.user_transport
97
94
 
98
 
    def __init__(self, possible_transports=None):
 
95
    def __init__(self, *ignored, **ignored_too):
99
96
        self.tags = self._format.make_tags(self)
100
97
        self._revision_history_cache = None
101
98
        self._revision_id_to_revno_cache = None
105
102
        self._last_revision_info_cache = None
106
103
        self._master_branch_cache = None
107
104
        self._merge_sorted_revisions_cache = None
108
 
        self._open_hook(possible_transports)
 
105
        self._open_hook()
109
106
        hooks = Branch.hooks['open']
110
107
        for hook in hooks:
111
108
            hook(self)
112
109
 
113
 
    def _open_hook(self, possible_transports):
 
110
    def _open_hook(self):
114
111
        """Called by init to allow simpler extension of the base class."""
115
112
 
116
 
    def _activate_fallback_location(self, url, possible_transports):
 
113
    def _activate_fallback_location(self, url):
117
114
        """Activate the branch/repository from url as a fallback repository."""
118
115
        for existing_fallback_repo in self.repository._fallback_repositories:
119
116
            if existing_fallback_repo.user_url == url:
120
117
                # This fallback is already configured.  This probably only
121
 
                # happens because ControlDir.sprout is a horrible mess.  To avoid
 
118
                # happens because BzrDir.sprout is a horrible mess.  To avoid
122
119
                # confusing _unstack we don't add this a second time.
123
120
                mutter('duplicate activation of fallback %r on %r', url, self)
124
121
                return
125
 
        repo = self._get_fallback_repository(url, possible_transports)
 
122
        repo = self._get_fallback_repository(url)
126
123
        if repo.has_same_location(self.repository):
127
124
            raise errors.UnstackableLocationError(self.user_url, url)
128
125
        self.repository.add_fallback_repository(repo)
182
179
        For instance, if the branch is at URL/.bzr/branch,
183
180
        Branch.open(URL) -> a Branch instance.
184
181
        """
185
 
        control = controldir.ControlDir.open(base,
186
 
            possible_transports=possible_transports, _unsupported=_unsupported)
187
 
        return control.open_branch(unsupported=_unsupported,
188
 
            possible_transports=possible_transports)
 
182
        control = bzrdir.BzrDir.open(base, _unsupported,
 
183
                                     possible_transports=possible_transports)
 
184
        return control.open_branch(unsupported=_unsupported)
189
185
 
190
186
    @staticmethod
191
 
    def open_from_transport(transport, name=None, _unsupported=False,
192
 
            possible_transports=None):
 
187
    def open_from_transport(transport, name=None, _unsupported=False):
193
188
        """Open the branch rooted at transport"""
194
 
        control = controldir.ControlDir.open_from_transport(transport, _unsupported)
195
 
        return control.open_branch(name=name, unsupported=_unsupported,
196
 
            possible_transports=possible_transports)
 
189
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
 
190
        return control.open_branch(name=name, unsupported=_unsupported)
197
191
 
198
192
    @staticmethod
199
193
    def open_containing(url, possible_transports=None):
207
201
        format, UnknownFormatError or UnsupportedFormatError are raised.
208
202
        If there is one, it is returned, along with the unused portion of url.
209
203
        """
210
 
        control, relpath = controldir.ControlDir.open_containing(url,
 
204
        control, relpath = bzrdir.BzrDir.open_containing(url,
211
205
                                                         possible_transports)
212
 
        branch = control.open_branch(possible_transports=possible_transports)
213
 
        return (branch, relpath)
 
206
        return control.open_branch(), relpath
214
207
 
215
208
    def _push_should_merge_tags(self):
216
209
        """Should _basic_push merge this branch's tags into the target?
228
221
 
229
222
        :return: A bzrlib.config.BranchConfig.
230
223
        """
231
 
        return _mod_config.BranchConfig(self)
232
 
 
233
 
    def get_config_stack(self):
234
 
        """Get a bzrlib.config.BranchStack for this Branch.
235
 
 
236
 
        This can then be used to get and set configuration options for the
237
 
        branch.
238
 
 
239
 
        :return: A bzrlib.config.BranchStack.
240
 
        """
241
 
        return _mod_config.BranchStack(self)
 
224
        return BranchConfig(self)
242
225
 
243
226
    def _get_config(self):
244
227
        """Get the concrete config for just the config in this branch.
252
235
        """
253
236
        raise NotImplementedError(self._get_config)
254
237
 
255
 
    def store_uncommitted(self, creator):
256
 
        """Store uncommitted changes from a ShelfCreator.
257
 
 
258
 
        :param creator: The ShelfCreator containing uncommitted changes, or
259
 
            None to delete any stored changes.
260
 
        :raises: ChangesAlreadyStored if the branch already has changes.
261
 
        """
262
 
        raise NotImplementedError(self.store_uncommitted)
263
 
 
264
 
    def get_unshelver(self, tree):
265
 
        """Return a shelf.Unshelver for this branch and tree.
266
 
 
267
 
        :param tree: The tree to use to construct the Unshelver.
268
 
        :return: an Unshelver or None if no changes are stored.
269
 
        """
270
 
        raise NotImplementedError(self.get_unshelver)
271
 
 
272
 
    def _get_fallback_repository(self, url, possible_transports):
 
238
    def _get_fallback_repository(self, url):
273
239
        """Get the repository we fallback to at url."""
274
240
        url = urlutils.join(self.base, url)
275
 
        a_branch = Branch.open(url, possible_transports=possible_transports)
 
241
        a_branch = Branch.open(url,
 
242
            possible_transports=[self.bzrdir.root_transport])
276
243
        return a_branch.repository
277
244
 
278
245
    @needs_read_lock
487
454
            after. If None, the rest of history is included.
488
455
        :param stop_rule: if stop_revision_id is not None, the precise rule
489
456
            to use for termination:
490
 
 
491
457
            * 'exclude' - leave the stop revision out of the result (default)
492
458
            * 'include' - the stop revision is the last item in the result
493
459
            * 'with-merges' - include the stop revision and all of its
495
461
            * 'with-merges-without-common-ancestry' - filter out revisions 
496
462
              that are in both ancestries
497
463
        :param direction: either 'reverse' or 'forward':
498
 
 
499
464
            * reverse means return the start_revision_id first, i.e.
500
465
              start at the most recent revision and go backwards in history
501
466
            * forward returns tuples in the opposite order to reverse.
545
510
        rev_iter = iter(merge_sorted_revisions)
546
511
        if start_revision_id is not None:
547
512
            for node in rev_iter:
548
 
                rev_id = node.key
 
513
                rev_id = node.key[-1]
549
514
                if rev_id != start_revision_id:
550
515
                    continue
551
516
                else:
552
517
                    # The decision to include the start or not
553
518
                    # depends on the stop_rule if a stop is provided
554
519
                    # so pop this node back into the iterator
555
 
                    rev_iter = itertools.chain(iter([node]), rev_iter)
 
520
                    rev_iter = chain(iter([node]), rev_iter)
556
521
                    break
557
522
        if stop_revision_id is None:
558
523
            # Yield everything
559
524
            for node in rev_iter:
560
 
                rev_id = node.key
 
525
                rev_id = node.key[-1]
561
526
                yield (rev_id, node.merge_depth, node.revno,
562
527
                       node.end_of_merge)
563
528
        elif stop_rule == 'exclude':
564
529
            for node in rev_iter:
565
 
                rev_id = node.key
 
530
                rev_id = node.key[-1]
566
531
                if rev_id == stop_revision_id:
567
532
                    return
568
533
                yield (rev_id, node.merge_depth, node.revno,
569
534
                       node.end_of_merge)
570
535
        elif stop_rule == 'include':
571
536
            for node in rev_iter:
572
 
                rev_id = node.key
 
537
                rev_id = node.key[-1]
573
538
                yield (rev_id, node.merge_depth, node.revno,
574
539
                       node.end_of_merge)
575
540
                if rev_id == stop_revision_id:
581
546
            ancestors = graph.find_unique_ancestors(start_revision_id,
582
547
                                                    [stop_revision_id])
583
548
            for node in rev_iter:
584
 
                rev_id = node.key
 
549
                rev_id = node.key[-1]
585
550
                if rev_id not in ancestors:
586
551
                    continue
587
552
                yield (rev_id, node.merge_depth, node.revno,
597
562
            reached_stop_revision_id = False
598
563
            revision_id_whitelist = []
599
564
            for node in rev_iter:
600
 
                rev_id = node.key
 
565
                rev_id = node.key[-1]
601
566
                if rev_id == left_parent:
602
567
                    # reached the left parent after the stop_revision
603
568
                    return
683
648
        """
684
649
        raise errors.UpgradeRequired(self.user_url)
685
650
 
686
 
    def get_append_revisions_only(self):
687
 
        """Whether it is only possible to append revisions to the history.
688
 
        """
689
 
        if not self._format.supports_set_append_revisions_only():
690
 
            return False
691
 
        return self.get_config_stack().get('append_revisions_only')
692
 
 
693
651
    def set_append_revisions_only(self, enabled):
694
652
        if not self._format.supports_set_append_revisions_only():
695
653
            raise errors.UpgradeRequired(self.user_url)
696
 
        self.get_config_stack().set('append_revisions_only', enabled)
 
654
        if enabled:
 
655
            value = 'True'
 
656
        else:
 
657
            value = 'False'
 
658
        self.get_config().set_user_option('append_revisions_only', value,
 
659
            warn_masked=True)
697
660
 
698
661
    def set_reference_info(self, file_id, tree_path, branch_location):
699
662
        """Set the branch location to use for a tree reference."""
704
667
        raise errors.UnsupportedOperation(self.get_reference_info, self)
705
668
 
706
669
    @needs_write_lock
707
 
    def fetch(self, from_branch, last_revision=None, limit=None):
 
670
    def fetch(self, from_branch, last_revision=None):
708
671
        """Copy revisions from from_branch into this branch.
709
672
 
710
673
        :param from_branch: Where to copy from.
711
674
        :param last_revision: What revision to stop at (None for at the end
712
675
                              of the branch.
713
 
        :param limit: Optional rough limit of revisions to fetch
714
676
        :return: None
715
677
        """
716
 
        return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
 
678
        return InterBranch.get(from_branch, self).fetch(last_revision)
717
679
 
718
680
    def get_bound_location(self):
719
681
        """Return the URL of the branch we are bound to.
728
690
        """
729
691
        raise errors.UpgradeRequired(self.user_url)
730
692
 
731
 
    def get_commit_builder(self, parents, config_stack=None, timestamp=None,
 
693
    def get_commit_builder(self, parents, config=None, timestamp=None,
732
694
                           timezone=None, committer=None, revprops=None,
733
695
                           revision_id=None, lossy=False):
734
696
        """Obtain a CommitBuilder for this branch.
744
706
            represented, when pushing to a foreign VCS 
745
707
        """
746
708
 
747
 
        if config_stack is None:
748
 
            config_stack = self.get_config_stack()
 
709
        if config is None:
 
710
            config = self.get_config()
749
711
 
750
 
        return self.repository.get_commit_builder(self, parents, config_stack,
 
712
        return self.repository.get_commit_builder(self, parents, config,
751
713
            timestamp, timezone, committer, revprops, revision_id,
752
714
            lossy)
753
715
 
758
720
        """
759
721
        return None
760
722
 
761
 
    @deprecated_method(deprecated_in((2, 5, 0)))
762
723
    def get_revision_delta(self, revno):
763
724
        """Return the delta for one revision.
764
725
 
765
726
        The delta is relative to its mainline predecessor, or the
766
727
        empty tree for revision 1.
767
728
        """
768
 
        try:
769
 
            revid = self.get_rev_id(revno)
770
 
        except errors.NoSuchRevision:
 
729
        rh = self.revision_history()
 
730
        if not (1 <= revno <= len(rh)):
771
731
            raise errors.InvalidRevisionNumber(revno)
772
 
        return self.repository.get_revision_delta(revid)
 
732
        return self.repository.get_revision_delta(rh[revno-1])
773
733
 
774
734
    def get_stacked_on_url(self):
775
735
        """Get the URL this branch is stacked against.
784
744
        """Print `file` to stdout."""
785
745
        raise NotImplementedError(self.print_file)
786
746
 
 
747
    @deprecated_method(deprecated_in((2, 4, 0)))
 
748
    def set_revision_history(self, rev_history):
 
749
        """See Branch.set_revision_history."""
 
750
        self._set_revision_history(rev_history)
 
751
 
 
752
    @needs_write_lock
 
753
    def _set_revision_history(self, rev_history):
 
754
        if len(rev_history) == 0:
 
755
            revid = _mod_revision.NULL_REVISION
 
756
        else:
 
757
            revid = rev_history[-1]
 
758
        if rev_history != self._lefthand_history(revid):
 
759
            raise errors.NotLefthandHistory(rev_history)
 
760
        self.set_last_revision_info(len(rev_history), revid)
 
761
        self._cache_revision_history(rev_history)
 
762
        for hook in Branch.hooks['set_rh']:
 
763
            hook(self, rev_history)
 
764
 
787
765
    @needs_write_lock
788
766
    def set_last_revision_info(self, revno, revision_id):
789
767
        """Set the last revision of this branch.
796
774
        configured to check constraints on history, in which case this may not
797
775
        be permitted.
798
776
        """
799
 
        raise NotImplementedError(self.set_last_revision_info)
 
777
        raise NotImplementedError(self.last_revision_info)
800
778
 
801
779
    @needs_write_lock
802
780
    def generate_revision_history(self, revision_id, last_rev=None,
803
781
                                  other_branch=None):
804
782
        """See Branch.generate_revision_history"""
805
783
        graph = self.repository.get_graph()
806
 
        (last_revno, last_revid) = self.last_revision_info()
807
784
        known_revision_ids = [
808
 
            (last_revid, last_revno),
 
785
            self.last_revision_info(),
809
786
            (_mod_revision.NULL_REVISION, 0),
810
787
            ]
811
788
        if last_rev is not None:
856
833
                return
857
834
            self._unstack()
858
835
        else:
859
 
            self._activate_fallback_location(url,
860
 
                possible_transports=[self.bzrdir.root_transport])
 
836
            self._activate_fallback_location(url)
861
837
        # write this out after the repository is stacked to avoid setting a
862
838
        # stacked config that doesn't work.
863
839
        self._set_config_location('stacked_on_location', url)
869
845
        """
870
846
        pb = ui.ui_factory.nested_progress_bar()
871
847
        try:
872
 
            pb.update(gettext("Unstacking"))
 
848
            pb.update("Unstacking")
873
849
            # The basic approach here is to fetch the tip of the branch,
874
850
            # including all available ghosts, from the existing stacked
875
851
            # repository into a new repository object without the fallbacks. 
889
865
            # stream from one of them to the other.  This does mean doing
890
866
            # separate SSH connection setup, but unstacking is not a
891
867
            # common operation so it's tolerable.
892
 
            new_bzrdir = controldir.ControlDir.open(
893
 
                self.bzrdir.root_transport.base)
 
868
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
894
869
            new_repository = new_bzrdir.find_repository()
895
870
            if new_repository._fallback_repositories:
896
871
                raise AssertionError("didn't expect %r to have "
939
914
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
940
915
                except errors.TagsNotSupported:
941
916
                    tags_to_fetch = set()
942
 
                fetch_spec = vf_search.NotInOtherForRevs(self.repository,
 
917
                fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
943
918
                    old_repository, required_ids=[self.last_revision()],
944
919
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
945
920
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
953
928
 
954
929
        :seealso: Branch._get_tags_bytes.
955
930
        """
956
 
        op = cleanup.OperationWithCleanups(self._set_tags_bytes_locked)
957
 
        op.add_cleanup(self.lock_write().unlock)
958
 
        return op.run_simple(bytes)
 
931
        return _run_with_write_locked_target(self, self._set_tags_bytes_locked,
 
932
                bytes)
959
933
 
960
934
    def _set_tags_bytes_locked(self, bytes):
961
935
        self._tags_bytes = bytes
986
960
        This means the next call to revision_history will need to call
987
961
        _gen_revision_history.
988
962
 
989
 
        This API is semi-public; it is only for use by subclasses, all other
990
 
        code should consider it to be private.
 
963
        This API is semi-public; it only for use by subclasses, all other code
 
964
        should consider it to be private.
991
965
        """
992
966
        self._revision_history_cache = None
993
967
        self._revision_id_to_revno_cache = None
1013
987
        """
1014
988
        raise NotImplementedError(self._gen_revision_history)
1015
989
 
1016
 
    def _revision_history(self):
 
990
    @needs_read_lock
 
991
    def revision_history(self):
 
992
        """Return sequence of revision ids on this branch.
 
993
 
 
994
        This method will cache the revision history for as long as it is safe to
 
995
        do so.
 
996
        """
1017
997
        if 'evil' in debug.debug_flags:
1018
998
            mutter_callsite(3, "revision_history scales with history.")
1019
999
        if self._revision_history_cache is not None:
1052
1032
    def _read_last_revision_info(self):
1053
1033
        raise NotImplementedError(self._read_last_revision_info)
1054
1034
 
 
1035
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1036
    def import_last_revision_info(self, source_repo, revno, revid):
 
1037
        """Set the last revision info, importing from another repo if necessary.
 
1038
 
 
1039
        :param source_repo: Source repository to optionally fetch from
 
1040
        :param revno: Revision number of the new tip
 
1041
        :param revid: Revision id of the new tip
 
1042
        """
 
1043
        if not self.repository.has_same_location(source_repo):
 
1044
            self.repository.fetch(source_repo, revision_id=revid)
 
1045
        self.set_last_revision_info(revno, revid)
 
1046
 
1055
1047
    def import_last_revision_info_and_tags(self, source, revno, revid,
1056
1048
                                           lossy=False):
1057
1049
        """Set the last revision info, importing from another repo if necessary.
1077
1069
        """Given a revision id, return its revno"""
1078
1070
        if _mod_revision.is_null(revision_id):
1079
1071
            return 0
1080
 
        history = self._revision_history()
 
1072
        history = self.revision_history()
1081
1073
        try:
1082
1074
            return history.index(revision_id) + 1
1083
1075
        except ValueError:
1148
1140
    def _set_config_location(self, name, url, config=None,
1149
1141
                             make_relative=False):
1150
1142
        if config is None:
1151
 
            config = self.get_config_stack()
 
1143
            config = self.get_config()
1152
1144
        if url is None:
1153
1145
            url = ''
1154
1146
        elif make_relative:
1155
1147
            url = urlutils.relative_url(self.base, url)
1156
 
        config.set(name, url)
 
1148
        config.set_user_option(name, url, warn_masked=True)
1157
1149
 
1158
1150
    def _get_config_location(self, name, config=None):
1159
1151
        if config is None:
1160
 
            config = self.get_config_stack()
1161
 
        location = config.get(name)
 
1152
            config = self.get_config()
 
1153
        location = config.get_user_option(name)
1162
1154
        if location == '':
1163
1155
            location = None
1164
1156
        return location
1165
1157
 
1166
1158
    def get_child_submit_format(self):
1167
1159
        """Return the preferred format of submissions to this branch."""
1168
 
        return self.get_config_stack().get('child_submit_format')
 
1160
        return self.get_config().get_user_option("child_submit_format")
1169
1161
 
1170
1162
    def get_submit_branch(self):
1171
1163
        """Return the submit location of the branch.
1174
1166
        pattern is that the user can override it by specifying a
1175
1167
        location.
1176
1168
        """
1177
 
        return self.get_config_stack().get('submit_branch')
 
1169
        return self.get_config().get_user_option('submit_branch')
1178
1170
 
1179
1171
    def set_submit_branch(self, location):
1180
1172
        """Return the submit location of the branch.
1183
1175
        pattern is that the user can override it by specifying a
1184
1176
        location.
1185
1177
        """
1186
 
        self.get_config_stack().set('submit_branch', location)
 
1178
        self.get_config().set_user_option('submit_branch', location,
 
1179
            warn_masked=True)
1187
1180
 
1188
1181
    def get_public_branch(self):
1189
1182
        """Return the public location of the branch.
1202
1195
        self._set_config_location('public_branch', location)
1203
1196
 
1204
1197
    def get_push_location(self):
1205
 
        """Return None or the location to push this branch to."""
1206
 
        return self.get_config_stack().get('push_location')
 
1198
        """Return the None or the location to push this branch to."""
 
1199
        push_loc = self.get_config().get_user_option('push_location')
 
1200
        return push_loc
1207
1201
 
1208
1202
    def set_push_location(self, location):
1209
1203
        """Set a new push location for this branch."""
1294
1288
            if repository_policy is not None:
1295
1289
                repository_policy.configure_branch(result)
1296
1290
            self.copy_content_into(result, revision_id=revision_id)
1297
 
            master_url = self.get_bound_location()
1298
 
            if master_url is None:
 
1291
            master_branch = self.get_master_branch()
 
1292
            if master_branch is None:
1299
1293
                result.set_parent(self.bzrdir.root_transport.base)
1300
1294
            else:
1301
 
                result.set_parent(master_url)
 
1295
                result.set_parent(master_branch.bzrdir.root_transport.base)
1302
1296
        finally:
1303
1297
            result.unlock()
1304
1298
        return result
1378
1372
        # TODO: We should probably also check that self.revision_history
1379
1373
        # matches the repository for older branch formats.
1380
1374
        # If looking for the code that cross-checks repository parents against
1381
 
        # the Graph.iter_lefthand_ancestry output, that is now a repository
 
1375
        # the iter_reverse_revision_history output, that is now a repository
1382
1376
        # specific check.
1383
1377
        return result
1384
1378
 
1385
 
    def _get_checkout_format(self, lightweight=False):
 
1379
    def _get_checkout_format(self):
1386
1380
        """Return the most suitable metadir for a checkout of this branch.
1387
1381
        Weaves are used if this branch's repository uses weaves.
1388
1382
        """
1423
1417
        :param to_location: The url to produce the checkout at
1424
1418
        :param revision_id: The revision to check out
1425
1419
        :param lightweight: If True, produce a lightweight checkout, otherwise,
1426
 
            produce a bound branch (heavyweight checkout)
 
1420
        produce a bound branch (heavyweight checkout)
1427
1421
        :param accelerator_tree: A tree which can be used for retrieving file
1428
1422
            contents more quickly than the revision tree, i.e. a workingtree.
1429
1423
            The revision tree will be used for cases where accelerator_tree's
1434
1428
        """
1435
1429
        t = transport.get_transport(to_location)
1436
1430
        t.ensure_base()
1437
 
        format = self._get_checkout_format(lightweight=lightweight)
1438
 
        try:
 
1431
        if lightweight:
 
1432
            format = self._get_checkout_format()
1439
1433
            checkout = format.initialize_on_transport(t)
1440
 
        except errors.AlreadyControlDirError:
1441
 
            # It's fine if the control directory already exists,
1442
 
            # as long as there is no existing branch and working tree.
1443
 
            checkout = controldir.ControlDir.open_from_transport(t)
1444
 
            try:
1445
 
                checkout.open_branch()
1446
 
            except errors.NotBranchError:
1447
 
                pass
1448
 
            else:
1449
 
                raise errors.AlreadyControlDirError(t.base)
1450
 
            if checkout.control_transport.base == self.bzrdir.control_transport.base:
1451
 
                # When checking out to the same control directory,
1452
 
                # always create a lightweight checkout
1453
 
                lightweight = True
1454
 
 
1455
 
        if lightweight:
1456
 
            from_branch = checkout.set_branch_reference(target_branch=self)
 
1434
            from_branch = BranchReferenceFormat().initialize(checkout, 
 
1435
                target_branch=self)
1457
1436
        else:
1458
 
            policy = checkout.determine_repository_policy()
1459
 
            repo = policy.acquire_repository()[0]
1460
 
            checkout_branch = checkout.create_branch()
 
1437
            format = self._get_checkout_format()
 
1438
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
 
1439
                to_location, force_new_tree=False, format=format)
 
1440
            checkout = checkout_branch.bzrdir
1461
1441
            checkout_branch.bind(self)
1462
1442
            # pull up to the specified revision_id to set the initial
1463
1443
            # branch tip correctly, and seed it with history.
1464
1444
            checkout_branch.pull(self, stop_revision=revision_id)
1465
 
            from_branch = None
 
1445
            from_branch=None
1466
1446
        tree = checkout.create_workingtree(revision_id,
1467
1447
                                           from_branch=from_branch,
1468
1448
                                           accelerator_tree=accelerator_tree,
1489
1469
 
1490
1470
    def reference_parent(self, file_id, path, possible_transports=None):
1491
1471
        """Return the parent branch for a tree-reference file_id
1492
 
 
1493
1472
        :param file_id: The file_id of the tree reference
1494
1473
        :param path: The path of the file_id in the tree
1495
1474
        :return: A branch associated with the file_id
1557
1536
            heads that must be fetched if present, but no error is necessary if
1558
1537
            they are not present.
1559
1538
        """
1560
 
        # For bzr native formats must_fetch is just the tip, and
1561
 
        # if_present_fetch are the tags.
 
1539
        # For bzr native formats must_fetch is just the tip, and if_present_fetch
 
1540
        # are the tags.
1562
1541
        must_fetch = set([self.last_revision()])
1563
 
        if_present_fetch = set()
1564
 
        if self.get_config_stack().get('branch.fetch_tags'):
1565
 
            try:
1566
 
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
1567
 
            except errors.TagsNotSupported:
1568
 
                pass
 
1542
        try:
 
1543
            if_present_fetch = set(self.tags.get_reverse_tag_dict())
 
1544
        except errors.TagsNotSupported:
 
1545
            if_present_fetch = set()
1569
1546
        must_fetch.discard(_mod_revision.NULL_REVISION)
1570
1547
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
1571
1548
        return must_fetch, if_present_fetch
1576
1553
 
1577
1554
    Formats provide three things:
1578
1555
     * An initialization routine,
1579
 
     * a format description
 
1556
     * a format string,
1580
1557
     * an open routine.
1581
1558
 
1582
1559
    Formats are placed in an dict by their format string for reference
1589
1566
    object will be created every time regardless.
1590
1567
    """
1591
1568
 
 
1569
    can_set_append_revisions_only = True
 
1570
 
1592
1571
    def __eq__(self, other):
1593
1572
        return self.__class__ is other.__class__
1594
1573
 
1595
1574
    def __ne__(self, other):
1596
1575
        return not (self == other)
1597
1576
 
1598
 
    def get_reference(self, controldir, name=None):
1599
 
        """Get the target reference of the branch in controldir.
 
1577
    @classmethod
 
1578
    def find_format(klass, a_bzrdir, name=None):
 
1579
        """Return the format for the branch object in a_bzrdir."""
 
1580
        try:
 
1581
            transport = a_bzrdir.get_branch_transport(None, name=name)
 
1582
            format_string = transport.get_bytes("format")
 
1583
            return format_registry.get(format_string)
 
1584
        except errors.NoSuchFile:
 
1585
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
 
1586
        except KeyError:
 
1587
            raise errors.UnknownFormatError(format=format_string, kind='branch')
 
1588
 
 
1589
    @classmethod
 
1590
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1591
    def get_default_format(klass):
 
1592
        """Return the current default format."""
 
1593
        return format_registry.get_default()
 
1594
 
 
1595
    @classmethod
 
1596
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1597
    def get_formats(klass):
 
1598
        """Get all the known formats.
 
1599
 
 
1600
        Warning: This triggers a load of all lazy registered formats: do not
 
1601
        use except when that is desireed.
 
1602
        """
 
1603
        return format_registry._get_all()
 
1604
 
 
1605
    def get_reference(self, a_bzrdir, name=None):
 
1606
        """Get the target reference of the branch in a_bzrdir.
1600
1607
 
1601
1608
        format probing must have been completed before calling
1602
1609
        this method - it is assumed that the format of the branch
1603
 
        in controldir is correct.
 
1610
        in a_bzrdir is correct.
1604
1611
 
1605
 
        :param controldir: The controldir to get the branch data from.
 
1612
        :param a_bzrdir: The bzrdir to get the branch data from.
1606
1613
        :param name: Name of the colocated branch to fetch
1607
1614
        :return: None if the branch is not a reference branch.
1608
1615
        """
1609
1616
        return None
1610
1617
 
1611
1618
    @classmethod
1612
 
    def set_reference(self, controldir, name, to_branch):
1613
 
        """Set the target reference of the branch in controldir.
 
1619
    def set_reference(self, a_bzrdir, name, to_branch):
 
1620
        """Set the target reference of the branch in a_bzrdir.
1614
1621
 
1615
1622
        format probing must have been completed before calling
1616
1623
        this method - it is assumed that the format of the branch
1617
 
        in controldir is correct.
 
1624
        in a_bzrdir is correct.
1618
1625
 
1619
 
        :param controldir: The controldir to set the branch reference for.
 
1626
        :param a_bzrdir: The bzrdir to set the branch reference for.
1620
1627
        :param name: Name of colocated branch to set, None for default
1621
1628
        :param to_branch: branch that the checkout is to reference
1622
1629
        """
1623
1630
        raise NotImplementedError(self.set_reference)
1624
1631
 
 
1632
    def get_format_string(self):
 
1633
        """Return the ASCII format string that identifies this format."""
 
1634
        raise NotImplementedError(self.get_format_string)
 
1635
 
1625
1636
    def get_format_description(self):
1626
1637
        """Return the short format description for this format."""
1627
1638
        raise NotImplementedError(self.get_format_description)
1628
1639
 
1629
 
    def _run_post_branch_init_hooks(self, controldir, name, branch):
 
1640
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
1630
1641
        hooks = Branch.hooks['post_branch_init']
1631
1642
        if not hooks:
1632
1643
            return
1633
 
        params = BranchInitHookParams(self, controldir, name, branch)
 
1644
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
1634
1645
        for hook in hooks:
1635
1646
            hook(params)
1636
1647
 
1637
 
    def initialize(self, controldir, name=None, repository=None,
1638
 
                   append_revisions_only=None):
1639
 
        """Create a branch of this format in controldir.
1640
 
 
 
1648
    def initialize(self, a_bzrdir, name=None, repository=None):
 
1649
        """Create a branch of this format in a_bzrdir.
 
1650
        
1641
1651
        :param name: Name of the colocated branch to create.
1642
1652
        """
1643
1653
        raise NotImplementedError(self.initialize)
1663
1673
        Note that it is normal for branch to be a RemoteBranch when using tags
1664
1674
        on a RemoteBranch.
1665
1675
        """
1666
 
        return _mod_tag.DisabledTags(branch)
 
1676
        return DisabledTags(branch)
1667
1677
 
1668
1678
    def network_name(self):
1669
1679
        """A simple byte string uniquely identifying this format for RPC calls.
1675
1685
        """
1676
1686
        raise NotImplementedError(self.network_name)
1677
1687
 
1678
 
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
1679
 
            found_repository=None, possible_transports=None):
1680
 
        """Return the branch object for controldir.
 
1688
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
 
1689
            found_repository=None):
 
1690
        """Return the branch object for a_bzrdir
1681
1691
 
1682
 
        :param controldir: A ControlDir that contains a branch.
 
1692
        :param a_bzrdir: A BzrDir that contains a branch.
1683
1693
        :param name: Name of colocated branch to open
1684
1694
        :param _found: a private parameter, do not use it. It is used to
1685
1695
            indicate if format probing has already be done.
1688
1698
        """
1689
1699
        raise NotImplementedError(self.open)
1690
1700
 
 
1701
    @classmethod
 
1702
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1703
    def register_format(klass, format):
 
1704
        """Register a metadir format.
 
1705
 
 
1706
        See MetaDirBranchFormatFactory for the ability to register a format
 
1707
        without loading the code the format needs until it is actually used.
 
1708
        """
 
1709
        format_registry.register(format)
 
1710
 
 
1711
    @classmethod
 
1712
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1713
    def set_default_format(klass, format):
 
1714
        format_registry.set_default(format)
 
1715
 
1691
1716
    def supports_set_append_revisions_only(self):
1692
1717
        """True if this format supports set_append_revisions_only."""
1693
1718
        return False
1700
1725
        """True if this format supports leaving locks in place."""
1701
1726
        return False # by default
1702
1727
 
 
1728
    @classmethod
 
1729
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1730
    def unregister_format(klass, format):
 
1731
        format_registry.remove(format)
 
1732
 
1703
1733
    def __str__(self):
1704
1734
        return self.get_format_description().rstrip()
1705
1735
 
1707
1737
        """True if this format supports tags stored in the branch"""
1708
1738
        return False  # by default
1709
1739
 
1710
 
    def tags_are_versioned(self):
1711
 
        """Whether the tag container for this branch versions tags."""
1712
 
        return False
1713
 
 
1714
 
    def supports_tags_referencing_ghosts(self):
1715
 
        """True if tags can reference ghost revisions."""
1716
 
        return True
1717
 
 
1718
1740
 
1719
1741
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1720
1742
    """A factory for a BranchFormat object, permitting simple lazy registration.
1734
1756
        """
1735
1757
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
1736
1758
        self._format_string = format_string
1737
 
 
 
1759
        
1738
1760
    def get_format_string(self):
1739
1761
        """See BranchFormat.get_format_string."""
1740
1762
        return self._format_string
1747
1769
class BranchHooks(Hooks):
1748
1770
    """A dictionary mapping hook name to a list of callables for branch hooks.
1749
1771
 
1750
 
    e.g. ['post_push'] Is the list of items to be called when the
1751
 
    push function is invoked.
 
1772
    e.g. ['set_rh'] Is the list of items to be called when the
 
1773
    set_revision_history function is invoked.
1752
1774
    """
1753
1775
 
1754
1776
    def __init__(self):
1758
1780
        notified.
1759
1781
        """
1760
1782
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
 
1783
        self.add_hook('set_rh',
 
1784
            "Invoked whenever the revision history has been set via "
 
1785
            "set_revision_history. The api signature is (branch, "
 
1786
            "revision_history), and the branch will be write-locked. "
 
1787
            "The set_rh hook can be expensive for bzr to trigger, a better "
 
1788
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
1761
1789
        self.add_hook('open',
1762
1790
            "Called with the Branch object that has been opened after a "
1763
1791
            "branch is opened.", (1, 8))
1836
1864
 
1837
1865
 
1838
1866
class ChangeBranchTipParams(object):
1839
 
    """Object holding parameters passed to `*_change_branch_tip` hooks.
 
1867
    """Object holding parameters passed to *_change_branch_tip hooks.
1840
1868
 
1841
1869
    There are 5 fields that hooks may wish to access:
1842
1870
 
1874
1902
 
1875
1903
 
1876
1904
class BranchInitHookParams(object):
1877
 
    """Object holding parameters passed to `*_branch_init` hooks.
 
1905
    """Object holding parameters passed to *_branch_init hooks.
1878
1906
 
1879
1907
    There are 4 fields that hooks may wish to access:
1880
1908
 
1881
1909
    :ivar format: the branch format
1882
 
    :ivar bzrdir: the ControlDir where the branch will be/has been initialized
 
1910
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
1883
1911
    :ivar name: name of colocated branch, if any (or None)
1884
1912
    :ivar branch: the branch created
1885
1913
 
1888
1916
    branch, which refer to the original branch.
1889
1917
    """
1890
1918
 
1891
 
    def __init__(self, format, controldir, name, branch):
 
1919
    def __init__(self, format, a_bzrdir, name, branch):
1892
1920
        """Create a group of BranchInitHook parameters.
1893
1921
 
1894
1922
        :param format: the branch format
1895
 
        :param controldir: the ControlDir where the branch will be/has been
 
1923
        :param a_bzrdir: the BzrDir where the branch will be/has been
1896
1924
            initialized
1897
1925
        :param name: name of colocated branch, if any (or None)
1898
1926
        :param branch: the branch created
1902
1930
        in branch, which refer to the original branch.
1903
1931
        """
1904
1932
        self.format = format
1905
 
        self.bzrdir = controldir
 
1933
        self.bzrdir = a_bzrdir
1906
1934
        self.name = name
1907
1935
        self.branch = branch
1908
1936
 
1914
1942
 
1915
1943
 
1916
1944
class SwitchHookParams(object):
1917
 
    """Object holding parameters passed to `*_switch` hooks.
 
1945
    """Object holding parameters passed to *_switch hooks.
1918
1946
 
1919
1947
    There are 4 fields that hooks may wish to access:
1920
1948
 
1921
 
    :ivar control_dir: ControlDir of the checkout to change
 
1949
    :ivar control_dir: BzrDir of the checkout to change
1922
1950
    :ivar to_branch: branch that the checkout is to reference
1923
1951
    :ivar force: skip the check for local commits in a heavy checkout
1924
1952
    :ivar revision_id: revision ID to switch to (or None)
1927
1955
    def __init__(self, control_dir, to_branch, force, revision_id):
1928
1956
        """Create a group of SwitchHook parameters.
1929
1957
 
1930
 
        :param control_dir: ControlDir of the checkout to change
 
1958
        :param control_dir: BzrDir of the checkout to change
1931
1959
        :param to_branch: branch that the checkout is to reference
1932
1960
        :param force: skip the check for local commits in a heavy checkout
1933
1961
        :param revision_id: revision ID to switch to (or None)
1946
1974
            self.revision_id)
1947
1975
 
1948
1976
 
1949
 
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
1950
 
    """Base class for branch formats that live in meta directories.
1951
 
    """
1952
 
 
1953
 
    def __init__(self):
1954
 
        BranchFormat.__init__(self)
1955
 
        bzrdir.BzrFormat.__init__(self)
1956
 
 
1957
 
    @classmethod
1958
 
    def find_format(klass, controldir, name=None):
1959
 
        """Return the format for the branch object in controldir."""
1960
 
        try:
1961
 
            transport = controldir.get_branch_transport(None, name=name)
1962
 
        except errors.NoSuchFile:
1963
 
            raise errors.NotBranchError(path=name, bzrdir=controldir)
1964
 
        try:
1965
 
            format_string = transport.get_bytes("format")
1966
 
        except errors.NoSuchFile:
1967
 
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1968
 
        return klass._find_format(format_registry, 'branch', format_string)
 
1977
class BranchFormatMetadir(BranchFormat):
 
1978
    """Common logic for meta-dir based branch formats."""
1969
1979
 
1970
1980
    def _branch_class(self):
1971
1981
        """What class to instantiate on open calls."""
1972
1982
        raise NotImplementedError(self._branch_class)
1973
1983
 
1974
 
    def _get_initial_config(self, append_revisions_only=None):
1975
 
        if append_revisions_only:
1976
 
            return "append_revisions_only = True\n"
1977
 
        else:
1978
 
            # Avoid writing anything if append_revisions_only is disabled,
1979
 
            # as that is the default.
1980
 
            return ""
1981
 
 
1982
1984
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1983
1985
                           repository=None):
1984
 
        """Initialize a branch in a control dir, with specified files
 
1986
        """Initialize a branch in a bzrdir, with specified files
1985
1987
 
1986
1988
        :param a_bzrdir: The bzrdir to initialize the branch in
1987
1989
        :param utf8_files: The files to create as a list of
1989
1991
        :param name: Name of colocated branch to create, if any
1990
1992
        :return: a branch in this format
1991
1993
        """
1992
 
        if name is None:
1993
 
            name = a_bzrdir._get_selected_branch()
1994
1994
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1995
1995
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1996
1996
        control_files = lockable_files.LockableFiles(branch_transport,
1998
1998
        control_files.create_lock()
1999
1999
        control_files.lock_write()
2000
2000
        try:
2001
 
            utf8_files += [('format', self.as_string())]
 
2001
            utf8_files += [('format', self.get_format_string())]
2002
2002
            for (filename, content) in utf8_files:
2003
2003
                branch_transport.put_bytes(
2004
2004
                    filename, content,
2010
2010
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2011
2011
        return branch
2012
2012
 
 
2013
    def network_name(self):
 
2014
        """A simple byte string uniquely identifying this format for RPC calls.
 
2015
 
 
2016
        Metadir branch formats use their format string.
 
2017
        """
 
2018
        return self.get_format_string()
 
2019
 
2013
2020
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2014
 
            found_repository=None, possible_transports=None):
 
2021
            found_repository=None):
2015
2022
        """See BranchFormat.open()."""
2016
 
        if name is None:
2017
 
            name = a_bzrdir._get_selected_branch()
2018
2023
        if not _found:
2019
 
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
 
2024
            format = BranchFormat.find_format(a_bzrdir, name=name)
2020
2025
            if format.__class__ != self.__class__:
2021
2026
                raise AssertionError("wrong format %r found for %r" %
2022
2027
                    (format, self))
2031
2036
                              name=name,
2032
2037
                              a_bzrdir=a_bzrdir,
2033
2038
                              _repository=found_repository,
2034
 
                              ignore_fallbacks=ignore_fallbacks,
2035
 
                              possible_transports=possible_transports)
 
2039
                              ignore_fallbacks=ignore_fallbacks)
2036
2040
        except errors.NoSuchFile:
2037
2041
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2038
2042
 
2039
 
    @property
2040
 
    def _matchingbzrdir(self):
2041
 
        ret = bzrdir.BzrDirMetaFormat1()
2042
 
        ret.set_branch_format(self)
2043
 
        return ret
 
2043
    def __init__(self):
 
2044
        super(BranchFormatMetadir, self).__init__()
 
2045
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
2046
        self._matchingbzrdir.set_branch_format(self)
2044
2047
 
2045
2048
    def supports_tags(self):
2046
2049
        return True
2048
2051
    def supports_leaving_lock(self):
2049
2052
        return True
2050
2053
 
2051
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
2052
 
            basedir=None):
2053
 
        BranchFormat.check_support_status(self,
2054
 
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
2055
 
            basedir=basedir)
2056
 
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
2057
 
            recommend_upgrade=recommend_upgrade, basedir=basedir)
 
2054
 
 
2055
class BzrBranchFormat5(BranchFormatMetadir):
 
2056
    """Bzr branch format 5.
 
2057
 
 
2058
    This format has:
 
2059
     - a revision-history file.
 
2060
     - a format string
 
2061
     - a lock dir guarding the branch itself
 
2062
     - all of this stored in a branch/ subdirectory
 
2063
     - works with shared repositories.
 
2064
 
 
2065
    This format is new in bzr 0.8.
 
2066
    """
 
2067
 
 
2068
    def _branch_class(self):
 
2069
        return BzrBranch5
 
2070
 
 
2071
    def get_format_string(self):
 
2072
        """See BranchFormat.get_format_string()."""
 
2073
        return "Bazaar-NG branch format 5\n"
 
2074
 
 
2075
    def get_format_description(self):
 
2076
        """See BranchFormat.get_format_description()."""
 
2077
        return "Branch format 5"
 
2078
 
 
2079
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2080
        """Create a branch of this format in a_bzrdir."""
 
2081
        utf8_files = [('revision-history', ''),
 
2082
                      ('branch-name', ''),
 
2083
                      ]
 
2084
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2085
 
 
2086
    def supports_tags(self):
 
2087
        return False
2058
2088
 
2059
2089
 
2060
2090
class BzrBranchFormat6(BranchFormatMetadir):
2071
2101
    def _branch_class(self):
2072
2102
        return BzrBranch6
2073
2103
 
2074
 
    @classmethod
2075
 
    def get_format_string(cls):
 
2104
    def get_format_string(self):
2076
2105
        """See BranchFormat.get_format_string()."""
2077
2106
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
2078
2107
 
2080
2109
        """See BranchFormat.get_format_description()."""
2081
2110
        return "Branch format 6"
2082
2111
 
2083
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2084
 
                   append_revisions_only=None):
 
2112
    def initialize(self, a_bzrdir, name=None, repository=None):
2085
2113
        """Create a branch of this format in a_bzrdir."""
2086
2114
        utf8_files = [('last-revision', '0 null:\n'),
2087
 
                      ('branch.conf',
2088
 
                          self._get_initial_config(append_revisions_only)),
 
2115
                      ('branch.conf', ''),
2089
2116
                      ('tags', ''),
2090
2117
                      ]
2091
2118
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2092
2119
 
2093
2120
    def make_tags(self, branch):
2094
2121
        """See bzrlib.branch.BranchFormat.make_tags()."""
2095
 
        return _mod_tag.BasicTags(branch)
 
2122
        return BasicTags(branch)
2096
2123
 
2097
2124
    def supports_set_append_revisions_only(self):
2098
2125
        return True
2104
2131
    def _branch_class(self):
2105
2132
        return BzrBranch8
2106
2133
 
2107
 
    @classmethod
2108
 
    def get_format_string(cls):
 
2134
    def get_format_string(self):
2109
2135
        """See BranchFormat.get_format_string()."""
2110
2136
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2111
2137
 
2113
2139
        """See BranchFormat.get_format_description()."""
2114
2140
        return "Branch format 8"
2115
2141
 
2116
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2117
 
                   append_revisions_only=None):
 
2142
    def initialize(self, a_bzrdir, name=None, repository=None):
2118
2143
        """Create a branch of this format in a_bzrdir."""
2119
2144
        utf8_files = [('last-revision', '0 null:\n'),
2120
 
                      ('branch.conf',
2121
 
                          self._get_initial_config(append_revisions_only)),
 
2145
                      ('branch.conf', ''),
2122
2146
                      ('tags', ''),
2123
2147
                      ('references', '')
2124
2148
                      ]
2126
2150
 
2127
2151
    def make_tags(self, branch):
2128
2152
        """See bzrlib.branch.BranchFormat.make_tags()."""
2129
 
        return _mod_tag.BasicTags(branch)
 
2153
        return BasicTags(branch)
2130
2154
 
2131
2155
    def supports_set_append_revisions_only(self):
2132
2156
        return True
2146
2170
    This format was introduced in bzr 1.6.
2147
2171
    """
2148
2172
 
2149
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2150
 
                   append_revisions_only=None):
 
2173
    def initialize(self, a_bzrdir, name=None, repository=None):
2151
2174
        """Create a branch of this format in a_bzrdir."""
2152
2175
        utf8_files = [('last-revision', '0 null:\n'),
2153
 
                      ('branch.conf',
2154
 
                          self._get_initial_config(append_revisions_only)),
 
2176
                      ('branch.conf', ''),
2155
2177
                      ('tags', ''),
2156
2178
                      ]
2157
2179
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2159
2181
    def _branch_class(self):
2160
2182
        return BzrBranch7
2161
2183
 
2162
 
    @classmethod
2163
 
    def get_format_string(cls):
 
2184
    def get_format_string(self):
2164
2185
        """See BranchFormat.get_format_string()."""
2165
2186
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2166
2187
 
2176
2197
 
2177
2198
    def make_tags(self, branch):
2178
2199
        """See bzrlib.branch.BranchFormat.make_tags()."""
2179
 
        return _mod_tag.BasicTags(branch)
 
2200
        return BasicTags(branch)
2180
2201
 
2181
2202
    supports_reference_locations = False
2182
2203
 
2183
2204
 
2184
 
class BranchReferenceFormat(BranchFormatMetadir):
 
2205
class BranchReferenceFormat(BranchFormat):
2185
2206
    """Bzr branch reference format.
2186
2207
 
2187
2208
    Branch references are used in implementing checkouts, they
2192
2213
     - a format string
2193
2214
    """
2194
2215
 
2195
 
    @classmethod
2196
 
    def get_format_string(cls):
 
2216
    def get_format_string(self):
2197
2217
        """See BranchFormat.get_format_string()."""
2198
2218
        return "Bazaar-NG Branch Reference Format 1\n"
2199
2219
 
2212
2232
        location = transport.put_bytes('location', to_branch.base)
2213
2233
 
2214
2234
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2215
 
            repository=None, append_revisions_only=None):
 
2235
            repository=None):
2216
2236
        """Create a branch of this format in a_bzrdir."""
2217
2237
        if target_branch is None:
2218
2238
            # this format does not implement branch itself, thus the implicit
2219
2239
            # creation contract must see it as uninitializable
2220
2240
            raise errors.UninitializableFormat(self)
2221
2241
        mutter('creating branch reference in %s', a_bzrdir.user_url)
2222
 
        if a_bzrdir._format.fixed_components:
2223
 
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
2224
 
        if name is None:
2225
 
            name = a_bzrdir._get_selected_branch()
2226
2242
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2227
2243
        branch_transport.put_bytes('location',
2228
 
            target_branch.user_url)
2229
 
        branch_transport.put_bytes('format', self.as_string())
2230
 
        branch = self.open(a_bzrdir, name, _found=True,
 
2244
            target_branch.bzrdir.user_url)
 
2245
        branch_transport.put_bytes('format', self.get_format_string())
 
2246
        branch = self.open(
 
2247
            a_bzrdir, name, _found=True,
2231
2248
            possible_transports=[target_branch.bzrdir.root_transport])
2232
2249
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2233
2250
        return branch
2234
2251
 
 
2252
    def __init__(self):
 
2253
        super(BranchReferenceFormat, self).__init__()
 
2254
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
2255
        self._matchingbzrdir.set_branch_format(self)
 
2256
 
2235
2257
    def _make_reference_clone_function(format, a_branch):
2236
2258
        """Create a clone() routine for a branch dynamically."""
2237
2259
        def clone(to_bzrdir, revision_id=None,
2259
2281
            a_bzrdir.
2260
2282
        :param possible_transports: An optional reusable transports list.
2261
2283
        """
2262
 
        if name is None:
2263
 
            name = a_bzrdir._get_selected_branch()
2264
2284
        if not _found:
2265
 
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
 
2285
            format = BranchFormat.find_format(a_bzrdir, name=name)
2266
2286
            if format.__class__ != self.__class__:
2267
2287
                raise AssertionError("wrong format %r found for %r" %
2268
2288
                    (format, self))
2269
2289
        if location is None:
2270
2290
            location = self.get_reference(a_bzrdir, name)
2271
 
        real_bzrdir = controldir.ControlDir.open(
 
2291
        real_bzrdir = bzrdir.BzrDir.open(
2272
2292
            location, possible_transports=possible_transports)
2273
 
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
2274
 
            possible_transports=possible_transports)
 
2293
        result = real_bzrdir.open_branch(name=name, 
 
2294
            ignore_fallbacks=ignore_fallbacks)
2275
2295
        # this changes the behaviour of result.clone to create a new reference
2276
2296
        # rather than a copy of the content of the branch.
2277
2297
        # I did not use a proxy object because that needs much more extensive
2311
2331
 
2312
2332
# formats which have no format string are not discoverable
2313
2333
# and not independently creatable, so are not registered.
 
2334
__format5 = BzrBranchFormat5()
2314
2335
__format6 = BzrBranchFormat6()
2315
2336
__format7 = BzrBranchFormat7()
2316
2337
__format8 = BzrBranchFormat8()
2317
 
format_registry.register_lazy(
2318
 
    "Bazaar-NG branch format 5\n", "bzrlib.branchfmt.fullhistory", "BzrBranchFormat5")
 
2338
format_registry.register(__format5)
2319
2339
format_registry.register(BranchReferenceFormat())
2320
2340
format_registry.register(__format6)
2321
2341
format_registry.register(__format7)
2358
2378
 
2359
2379
    def __init__(self, _format=None,
2360
2380
                 _control_files=None, a_bzrdir=None, name=None,
2361
 
                 _repository=None, ignore_fallbacks=False,
2362
 
                 possible_transports=None):
 
2381
                 _repository=None, ignore_fallbacks=False):
2363
2382
        """Create new branch object at a particular location."""
2364
2383
        if a_bzrdir is None:
2365
2384
            raise ValueError('a_bzrdir must be supplied')
2366
 
        if name is None:
2367
 
            raise ValueError('name must be supplied')
2368
 
        self.bzrdir = a_bzrdir
2369
 
        self._user_transport = self.bzrdir.transport.clone('..')
2370
 
        if name != "":
2371
 
            self._user_transport.set_segment_parameter(
2372
 
                "branch", urlutils.escape(name))
2373
 
        self._base = self._user_transport.base
 
2385
        else:
 
2386
            self.bzrdir = a_bzrdir
 
2387
        self._base = self.bzrdir.transport.clone('..').base
2374
2388
        self.name = name
 
2389
        # XXX: We should be able to just do
 
2390
        #   self.base = self.bzrdir.root_transport.base
 
2391
        # but this does not quite work yet -- mbp 20080522
2375
2392
        self._format = _format
2376
2393
        if _control_files is None:
2377
2394
            raise ValueError('BzrBranch _control_files is None')
2378
2395
        self.control_files = _control_files
2379
2396
        self._transport = _control_files._transport
2380
2397
        self.repository = _repository
2381
 
        self.conf_store = None
2382
 
        Branch.__init__(self, possible_transports)
 
2398
        Branch.__init__(self)
2383
2399
 
2384
2400
    def __str__(self):
2385
 
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
 
2401
        if self.name is None:
 
2402
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
 
2403
        else:
 
2404
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
 
2405
                self.name)
2386
2406
 
2387
2407
    __repr__ = __str__
2388
2408
 
2392
2412
 
2393
2413
    base = property(_get_base, doc="The URL for the root of this branch.")
2394
2414
 
2395
 
    @property
2396
 
    def user_transport(self):
2397
 
        return self._user_transport
2398
 
 
2399
2415
    def _get_config(self):
2400
 
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
2401
 
 
2402
 
    def _get_config_store(self):
2403
 
        if self.conf_store is None:
2404
 
            self.conf_store =  _mod_config.BranchStore(self)
2405
 
        return self.conf_store
2406
 
 
2407
 
    def _uncommitted_branch(self):
2408
 
        """Return the branch that may contain uncommitted changes."""
2409
 
        master = self.get_master_branch()
2410
 
        if master is not None:
2411
 
            return master
2412
 
        else:
2413
 
            return self
2414
 
 
2415
 
    def store_uncommitted(self, creator):
2416
 
        """Store uncommitted changes from a ShelfCreator.
2417
 
 
2418
 
        :param creator: The ShelfCreator containing uncommitted changes, or
2419
 
            None to delete any stored changes.
2420
 
        :raises: ChangesAlreadyStored if the branch already has changes.
2421
 
        """
2422
 
        branch = self._uncommitted_branch()
2423
 
        if creator is None:
2424
 
            branch._transport.delete('stored-transform')
2425
 
            return
2426
 
        if branch._transport.has('stored-transform'):
2427
 
            raise errors.ChangesAlreadyStored
2428
 
        transform = StringIO()
2429
 
        creator.write_shelf(transform)
2430
 
        transform.seek(0)
2431
 
        branch._transport.put_file('stored-transform', transform)
2432
 
 
2433
 
    def get_unshelver(self, tree):
2434
 
        """Return a shelf.Unshelver for this branch and tree.
2435
 
 
2436
 
        :param tree: The tree to use to construct the Unshelver.
2437
 
        :return: an Unshelver or None if no changes are stored.
2438
 
        """
2439
 
        branch = self._uncommitted_branch()
2440
 
        try:
2441
 
            transform = branch._transport.get('stored-transform')
2442
 
        except errors.NoSuchFile:
2443
 
            return None
2444
 
        return shelf.Unshelver.from_tree_and_shelf(tree, transform)
 
2416
        return TransportConfig(self._transport, 'branch.conf')
2445
2417
 
2446
2418
    def is_locked(self):
2447
2419
        return self.control_files.is_locked()
2455
2427
        """
2456
2428
        if not self.is_locked():
2457
2429
            self._note_lock('w')
 
2430
        # All-in-one needs to always unlock/lock.
 
2431
        repo_control = getattr(self.repository, 'control_files', None)
 
2432
        if self.control_files == repo_control or not self.is_locked():
2458
2433
            self.repository._warn_if_deprecated(self)
2459
2434
            self.repository.lock_write()
2460
2435
            took_lock = True
2475
2450
        """
2476
2451
        if not self.is_locked():
2477
2452
            self._note_lock('r')
 
2453
        # All-in-one needs to always unlock/lock.
 
2454
        repo_control = getattr(self.repository, 'control_files', None)
 
2455
        if self.control_files == repo_control or not self.is_locked():
2478
2456
            self.repository._warn_if_deprecated(self)
2479
2457
            self.repository.lock_read()
2480
2458
            took_lock = True
2490
2468
 
2491
2469
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2492
2470
    def unlock(self):
2493
 
        if self.control_files._lock_count == 1 and self.conf_store is not None:
2494
 
            self.conf_store.save_changes()
2495
2471
        try:
2496
2472
            self.control_files.unlock()
2497
2473
        finally:
 
2474
            # All-in-one needs to always unlock/lock.
 
2475
            repo_control = getattr(self.repository, 'control_files', None)
 
2476
            if (self.control_files == repo_control or
 
2477
                not self.control_files.is_locked()):
 
2478
                self.repository.unlock()
2498
2479
            if not self.control_files.is_locked():
2499
 
                self.repository.unlock()
2500
2480
                # we just released the lock
2501
2481
                self._clear_cached_state()
2502
2482
 
2520
2500
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2521
2501
        revision_id = _mod_revision.ensure_null(revision_id)
2522
2502
        old_revno, old_revid = self.last_revision_info()
2523
 
        if self.get_append_revisions_only():
 
2503
        if self._get_append_revisions_only():
2524
2504
            self._check_history_violation(revision_id)
2525
2505
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2526
2506
        self._write_last_revision_info(revno, revision_id)
2669
2649
        self._transport.put_bytes('last-revision', out_string,
2670
2650
            mode=self.bzrdir._get_file_mode())
2671
2651
 
2672
 
    @needs_write_lock
2673
 
    def update_feature_flags(self, updated_flags):
2674
 
        """Update the feature flags for this branch.
2675
 
 
2676
 
        :param updated_flags: Dictionary mapping feature names to necessities
2677
 
            A necessity can be None to indicate the feature should be removed
 
2652
 
 
2653
class FullHistoryBzrBranch(BzrBranch):
 
2654
    """Bzr branch which contains the full revision history."""
 
2655
 
 
2656
    @needs_write_lock
 
2657
    def set_last_revision_info(self, revno, revision_id):
 
2658
        if not revision_id or not isinstance(revision_id, basestring):
 
2659
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2660
        revision_id = _mod_revision.ensure_null(revision_id)
 
2661
        # this old format stores the full history, but this api doesn't
 
2662
        # provide it, so we must generate, and might as well check it's
 
2663
        # correct
 
2664
        history = self._lefthand_history(revision_id)
 
2665
        if len(history) != revno:
 
2666
            raise AssertionError('%d != %d' % (len(history), revno))
 
2667
        self._set_revision_history(history)
 
2668
 
 
2669
    def _read_last_revision_info(self):
 
2670
        rh = self.revision_history()
 
2671
        revno = len(rh)
 
2672
        if revno:
 
2673
            return (revno, rh[-1])
 
2674
        else:
 
2675
            return (0, _mod_revision.NULL_REVISION)
 
2676
 
 
2677
    @deprecated_method(deprecated_in((2, 4, 0)))
 
2678
    @needs_write_lock
 
2679
    def set_revision_history(self, rev_history):
 
2680
        """See Branch.set_revision_history."""
 
2681
        self._set_revision_history(rev_history)
 
2682
 
 
2683
    def _set_revision_history(self, rev_history):
 
2684
        if 'evil' in debug.debug_flags:
 
2685
            mutter_callsite(3, "set_revision_history scales with history.")
 
2686
        check_not_reserved_id = _mod_revision.check_not_reserved_id
 
2687
        for rev_id in rev_history:
 
2688
            check_not_reserved_id(rev_id)
 
2689
        if Branch.hooks['post_change_branch_tip']:
 
2690
            # Don't calculate the last_revision_info() if there are no hooks
 
2691
            # that will use it.
 
2692
            old_revno, old_revid = self.last_revision_info()
 
2693
        if len(rev_history) == 0:
 
2694
            revid = _mod_revision.NULL_REVISION
 
2695
        else:
 
2696
            revid = rev_history[-1]
 
2697
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
 
2698
        self._write_revision_history(rev_history)
 
2699
        self._clear_cached_state()
 
2700
        self._cache_revision_history(rev_history)
 
2701
        for hook in Branch.hooks['set_rh']:
 
2702
            hook(self, rev_history)
 
2703
        if Branch.hooks['post_change_branch_tip']:
 
2704
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2705
 
 
2706
    def _write_revision_history(self, history):
 
2707
        """Factored out of set_revision_history.
 
2708
 
 
2709
        This performs the actual writing to disk.
 
2710
        It is intended to be called by set_revision_history."""
 
2711
        self._transport.put_bytes(
 
2712
            'revision-history', '\n'.join(history),
 
2713
            mode=self.bzrdir._get_file_mode())
 
2714
 
 
2715
    def _gen_revision_history(self):
 
2716
        history = self._transport.get_bytes('revision-history').split('\n')
 
2717
        if history[-1:] == ['']:
 
2718
            # There shouldn't be a trailing newline, but just in case.
 
2719
            history.pop()
 
2720
        return history
 
2721
 
 
2722
    def _synchronize_history(self, destination, revision_id):
 
2723
        if not isinstance(destination, FullHistoryBzrBranch):
 
2724
            super(BzrBranch, self)._synchronize_history(
 
2725
                destination, revision_id)
 
2726
            return
 
2727
        if revision_id == _mod_revision.NULL_REVISION:
 
2728
            new_history = []
 
2729
        else:
 
2730
            new_history = self.revision_history()
 
2731
        if revision_id is not None and new_history != []:
 
2732
            try:
 
2733
                new_history = new_history[:new_history.index(revision_id) + 1]
 
2734
            except ValueError:
 
2735
                rev = self.repository.get_revision(revision_id)
 
2736
                new_history = rev.get_history(self.repository)[1:]
 
2737
        destination._set_revision_history(new_history)
 
2738
 
 
2739
    @needs_write_lock
 
2740
    def generate_revision_history(self, revision_id, last_rev=None,
 
2741
        other_branch=None):
 
2742
        """Create a new revision history that will finish with revision_id.
 
2743
 
 
2744
        :param revision_id: the new tip to use.
 
2745
        :param last_rev: The previous last_revision. If not None, then this
 
2746
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
2747
        :param other_branch: The other branch that DivergedBranches should
 
2748
            raise with respect to.
2678
2749
        """
2679
 
        self._format._update_feature_flags(updated_flags)
2680
 
        self.control_transport.put_bytes('format', self._format.as_string())
 
2750
        self._set_revision_history(self._lefthand_history(revision_id,
 
2751
            last_rev, other_branch))
 
2752
 
 
2753
 
 
2754
class BzrBranch5(FullHistoryBzrBranch):
 
2755
    """A format 5 branch. This supports new features over plain branches.
 
2756
 
 
2757
    It has support for a master_branch which is the data for bound branches.
 
2758
    """
2681
2759
 
2682
2760
 
2683
2761
class BzrBranch8(BzrBranch):
2684
2762
    """A branch that stores tree-reference locations."""
2685
2763
 
2686
 
    def _open_hook(self, possible_transports=None):
 
2764
    def _open_hook(self):
2687
2765
        if self._ignore_fallbacks:
2688
2766
            return
2689
 
        if possible_transports is None:
2690
 
            possible_transports = [self.bzrdir.root_transport]
2691
2767
        try:
2692
2768
            url = self.get_stacked_on_url()
2693
2769
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2701
2777
                    raise AssertionError(
2702
2778
                        "'transform_fallback_location' hook %s returned "
2703
2779
                        "None, not a URL." % hook_name)
2704
 
            self._activate_fallback_location(url,
2705
 
                possible_transports=possible_transports)
 
2780
            self._activate_fallback_location(url)
2706
2781
 
2707
2782
    def __init__(self, *args, **kwargs):
2708
2783
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2826
2901
        """See Branch.set_push_location."""
2827
2902
        self._master_branch_cache = None
2828
2903
        result = None
2829
 
        conf = self.get_config_stack()
 
2904
        config = self.get_config()
2830
2905
        if location is None:
2831
 
            if not conf.get('bound'):
 
2906
            if config.get_user_option('bound') != 'True':
2832
2907
                return False
2833
2908
            else:
2834
 
                conf.set('bound', 'False')
 
2909
                config.set_user_option('bound', 'False', warn_masked=True)
2835
2910
                return True
2836
2911
        else:
2837
2912
            self._set_config_location('bound_location', location,
2838
 
                                      config=conf)
2839
 
            conf.set('bound', 'True')
 
2913
                                      config=config)
 
2914
            config.set_user_option('bound', 'True', warn_masked=True)
2840
2915
        return True
2841
2916
 
2842
2917
    def _get_bound_location(self, bound):
2843
2918
        """Return the bound location in the config file.
2844
2919
 
2845
2920
        Return None if the bound parameter does not match"""
2846
 
        conf = self.get_config_stack()
2847
 
        if conf.get('bound') != bound:
 
2921
        config = self.get_config()
 
2922
        config_bound = (config.get_user_option('bound') == 'True')
 
2923
        if config_bound != bound:
2848
2924
            return None
2849
 
        return self._get_config_location('bound_location', config=conf)
 
2925
        return self._get_config_location('bound_location', config=config)
2850
2926
 
2851
2927
    def get_bound_location(self):
2852
 
        """See Branch.get_bound_location."""
 
2928
        """See Branch.set_push_location."""
2853
2929
        return self._get_bound_location(True)
2854
2930
 
2855
2931
    def get_old_bound_location(self):
2860
2936
        # you can always ask for the URL; but you might not be able to use it
2861
2937
        # if the repo can't support stacking.
2862
2938
        ## self._check_stackable_repo()
2863
 
        # stacked_on_location is only ever defined in branch.conf, so don't
2864
 
        # waste effort reading the whole stack of config files.
2865
 
        conf = _mod_config.BranchOnlyStack(self)
2866
 
        stacked_url = self._get_config_location('stacked_on_location',
2867
 
                                                config=conf)
 
2939
        stacked_url = self._get_config_location('stacked_on_location')
2868
2940
        if stacked_url is None:
2869
2941
            raise errors.NotStacked(self)
2870
 
        return stacked_url.encode('utf-8')
 
2942
        return stacked_url
 
2943
 
 
2944
    def _get_append_revisions_only(self):
 
2945
        return self.get_config(
 
2946
            ).get_user_option_as_bool('append_revisions_only')
2871
2947
 
2872
2948
    @needs_read_lock
2873
2949
    def get_rev_id(self, revno, history=None):
2903
2979
            except errors.RevisionNotPresent, e:
2904
2980
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
2905
2981
            index = len(self._partial_revision_history_cache) - 1
2906
 
            if index < 0:
2907
 
                raise errors.NoSuchRevision(self, revision_id)
2908
2982
            if self._partial_revision_history_cache[index] != revision_id:
2909
2983
                raise errors.NoSuchRevision(self, revision_id)
2910
2984
        return self.revno() - index
2962
3036
    :ivar local_branch: target branch if there is a Master, else None
2963
3037
    :ivar target_branch: Target/destination branch object. (write locked)
2964
3038
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
2965
 
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
2966
3039
    """
2967
3040
 
 
3041
    @deprecated_method(deprecated_in((2, 3, 0)))
 
3042
    def __int__(self):
 
3043
        """Return the relative change in revno.
 
3044
 
 
3045
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3046
        """
 
3047
        return self.new_revno - self.old_revno
 
3048
 
2968
3049
    def report(self, to_file):
2969
 
        tag_conflicts = getattr(self, "tag_conflicts", None)
2970
 
        tag_updates = getattr(self, "tag_updates", None)
2971
3050
        if not is_quiet():
2972
 
            if self.old_revid != self.new_revid:
 
3051
            if self.old_revid == self.new_revid:
 
3052
                to_file.write('No revisions to pull.\n')
 
3053
            else:
2973
3054
                to_file.write('Now on revision %d.\n' % self.new_revno)
2974
 
            if tag_updates:
2975
 
                to_file.write('%d tag(s) updated.\n' % len(tag_updates))
2976
 
            if self.old_revid == self.new_revid and not tag_updates:
2977
 
                if not tag_conflicts:
2978
 
                    to_file.write('No revisions or tags to pull.\n')
2979
 
                else:
2980
 
                    to_file.write('No revisions to pull.\n')
2981
3055
        self._show_tag_conficts(to_file)
2982
3056
 
2983
3057
 
3000
3074
        target, otherwise it will be None.
3001
3075
    """
3002
3076
 
 
3077
    @deprecated_method(deprecated_in((2, 3, 0)))
 
3078
    def __int__(self):
 
3079
        """Return the relative change in revno.
 
3080
 
 
3081
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3082
        """
 
3083
        return self.new_revno - self.old_revno
 
3084
 
3003
3085
    def report(self, to_file):
3004
 
        # TODO: This function gets passed a to_file, but then
3005
 
        # ignores it and calls note() instead. This is also
3006
 
        # inconsistent with PullResult(), which writes to stdout.
3007
 
        # -- JRV20110901, bug #838853
3008
 
        tag_conflicts = getattr(self, "tag_conflicts", None)
3009
 
        tag_updates = getattr(self, "tag_updates", None)
3010
 
        if not is_quiet():
3011
 
            if self.old_revid != self.new_revid:
3012
 
                note(gettext('Pushed up to revision %d.') % self.new_revno)
3013
 
            if tag_updates:
3014
 
                note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
3015
 
            if self.old_revid == self.new_revid and not tag_updates:
3016
 
                if not tag_conflicts:
3017
 
                    note(gettext('No new revisions or tags to push.'))
3018
 
                else:
3019
 
                    note(gettext('No new revisions to push.'))
 
3086
        """Write a human-readable description of the result."""
 
3087
        if self.old_revid == self.new_revid:
 
3088
            note('No new revisions to push.')
 
3089
        else:
 
3090
            note('Pushed up to revision %d.' % self.new_revno)
3020
3091
        self._show_tag_conficts(to_file)
3021
3092
 
3022
3093
 
3036
3107
        :param verbose: Requests more detailed display of what was checked,
3037
3108
            if any.
3038
3109
        """
3039
 
        note(gettext('checked branch {0} format {1}').format(
3040
 
                                self.branch.user_url, self.branch._format))
 
3110
        note('checked branch %s format %s', self.branch.user_url,
 
3111
            self.branch._format)
3041
3112
        for error in self.errors:
3042
 
            note(gettext('found error:%s'), error)
 
3113
            note('found error:%s', error)
3043
3114
 
3044
3115
 
3045
3116
class Converter5to6(object):
3052
3123
 
3053
3124
        # Copy source data into target
3054
3125
        new_branch._write_last_revision_info(*branch.last_revision_info())
3055
 
        new_branch.lock_write()
3056
 
        try:
3057
 
            new_branch.set_parent(branch.get_parent())
3058
 
            new_branch.set_bound_location(branch.get_bound_location())
3059
 
            new_branch.set_push_location(branch.get_push_location())
3060
 
        finally:
3061
 
            new_branch.unlock()
 
3126
        new_branch.set_parent(branch.get_parent())
 
3127
        new_branch.set_bound_location(branch.get_bound_location())
 
3128
        new_branch.set_push_location(branch.get_push_location())
3062
3129
 
3063
3130
        # New branch has no tags by default
3064
3131
        new_branch.tags._set_tag_dict({})
3065
3132
 
3066
3133
        # Copying done; now update target format
3067
3134
        new_branch._transport.put_bytes('format',
3068
 
            format.as_string(),
 
3135
            format.get_format_string(),
3069
3136
            mode=new_branch.bzrdir._get_file_mode())
3070
3137
 
3071
3138
        # Clean up old files
3072
3139
        new_branch._transport.delete('revision-history')
3073
 
        branch.lock_write()
3074
3140
        try:
3075
 
            try:
3076
 
                branch.set_parent(None)
3077
 
            except errors.NoSuchFile:
3078
 
                pass
3079
 
            branch.set_bound_location(None)
3080
 
        finally:
3081
 
            branch.unlock()
 
3141
            branch.set_parent(None)
 
3142
        except errors.NoSuchFile:
 
3143
            pass
 
3144
        branch.set_bound_location(None)
3082
3145
 
3083
3146
 
3084
3147
class Converter6to7(object):
3088
3151
        format = BzrBranchFormat7()
3089
3152
        branch._set_config_location('stacked_on_location', '')
3090
3153
        # update target format
3091
 
        branch._transport.put_bytes('format', format.as_string())
 
3154
        branch._transport.put_bytes('format', format.get_format_string())
3092
3155
 
3093
3156
 
3094
3157
class Converter7to8(object):
3095
 
    """Perform an in-place upgrade of format 7 to format 8"""
 
3158
    """Perform an in-place upgrade of format 6 to format 7"""
3096
3159
 
3097
3160
    def convert(self, branch):
3098
3161
        format = BzrBranchFormat8()
3099
3162
        branch._transport.put_bytes('references', '')
3100
3163
        # update target format
3101
 
        branch._transport.put_bytes('format', format.as_string())
 
3164
        branch._transport.put_bytes('format', format.get_format_string())
 
3165
 
 
3166
 
 
3167
def _run_with_write_locked_target(target, callable, *args, **kwargs):
 
3168
    """Run ``callable(*args, **kwargs)``, write-locking target for the
 
3169
    duration.
 
3170
 
 
3171
    _run_with_write_locked_target will attempt to release the lock it acquires.
 
3172
 
 
3173
    If an exception is raised by callable, then that exception *will* be
 
3174
    propagated, even if the unlock attempt raises its own error.  Thus
 
3175
    _run_with_write_locked_target should be preferred to simply doing::
 
3176
 
 
3177
        target.lock_write()
 
3178
        try:
 
3179
            return callable(*args, **kwargs)
 
3180
        finally:
 
3181
            target.unlock()
 
3182
 
 
3183
    """
 
3184
    # This is very similar to bzrlib.decorators.needs_write_lock.  Perhaps they
 
3185
    # should share code?
 
3186
    target.lock_write()
 
3187
    try:
 
3188
        result = callable(*args, **kwargs)
 
3189
    except:
 
3190
        exc_info = sys.exc_info()
 
3191
        try:
 
3192
            target.unlock()
 
3193
        finally:
 
3194
            raise exc_info[0], exc_info[1], exc_info[2]
 
3195
    else:
 
3196
        target.unlock()
 
3197
        return result
3102
3198
 
3103
3199
 
3104
3200
class InterBranch(InterObject):
3152
3248
        raise NotImplementedError(self.copy_content_into)
3153
3249
 
3154
3250
    @needs_write_lock
3155
 
    def fetch(self, stop_revision=None, limit=None):
 
3251
    def fetch(self, stop_revision=None):
3156
3252
        """Fetch revisions.
3157
3253
 
3158
3254
        :param stop_revision: Last revision to fetch
3159
 
        :param limit: Optional rough limit of revisions to fetch
3160
3255
        """
3161
3256
        raise NotImplementedError(self.fetch)
3162
3257
 
3163
3258
 
3164
 
def _fix_overwrite_type(overwrite):
3165
 
    if isinstance(overwrite, bool):
3166
 
        if overwrite:
3167
 
            return ["history", "tags"]
3168
 
        else:
3169
 
            return []
3170
 
    return overwrite
3171
 
 
3172
 
 
3173
3259
class GenericInterBranch(InterBranch):
3174
3260
    """InterBranch implementation that uses public Branch functions."""
3175
3261
 
3209
3295
            self.source.tags.merge_to(self.target.tags)
3210
3296
 
3211
3297
    @needs_write_lock
3212
 
    def fetch(self, stop_revision=None, limit=None):
 
3298
    def fetch(self, stop_revision=None):
3213
3299
        if self.target.base == self.source.base:
3214
3300
            return (0, [])
3215
3301
        self.source.lock_read()
3220
3306
            fetch_spec_factory.source_repo = self.source.repository
3221
3307
            fetch_spec_factory.target_repo = self.target.repository
3222
3308
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3223
 
            fetch_spec_factory.limit = limit
3224
3309
            fetch_spec = fetch_spec_factory.make_fetch_spec()
3225
3310
            return self.target.repository.fetch(self.source.repository,
3226
3311
                fetch_spec=fetch_spec)
3280
3365
        if local and not bound_location:
3281
3366
            raise errors.LocalRequiresBoundBranch()
3282
3367
        master_branch = None
3283
 
        source_is_master = False
3284
 
        if bound_location:
3285
 
            # bound_location comes from a config file, some care has to be
3286
 
            # taken to relate it to source.user_url
3287
 
            normalized = urlutils.normalize_url(bound_location)
3288
 
            try:
3289
 
                relpath = self.source.user_transport.relpath(normalized)
3290
 
                source_is_master = (relpath == '')
3291
 
            except (errors.PathNotChild, errors.InvalidURL):
3292
 
                source_is_master = False
 
3368
        source_is_master = (self.source.user_url == bound_location)
3293
3369
        if not local and bound_location and not source_is_master:
3294
3370
            # not pulling from master, so we need to update master.
3295
3371
            master_branch = self.target.get_master_branch(possible_transports)
3314
3390
 
3315
3391
        This is the basic concrete implementation of push()
3316
3392
 
3317
 
        :param _override_hook_source_branch: If specified, run the hooks
3318
 
            passing this Branch as the source, rather than self.  This is for
3319
 
            use of RemoteBranch, where push is delegated to the underlying
3320
 
            vfs-based Branch.
 
3393
        :param _override_hook_source_branch: If specified, run
 
3394
        the hooks passing this Branch as the source, rather than self.
 
3395
        This is for use of RemoteBranch, where push is delegated to the
 
3396
        underlying vfs-based Branch.
3321
3397
        """
3322
3398
        if lossy:
3323
3399
            raise errors.LossyPushToSameVCS(self.source, self.target)
3324
3400
        # TODO: Public option to disable running hooks - should be trivial but
3325
3401
        # needs tests.
3326
 
 
3327
 
        op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3328
 
        op.add_cleanup(self.source.lock_read().unlock)
3329
 
        op.add_cleanup(self.target.lock_write().unlock)
3330
 
        return op.run(overwrite, stop_revision,
3331
 
            _override_hook_source_branch=_override_hook_source_branch)
 
3402
        self.source.lock_read()
 
3403
        try:
 
3404
            return _run_with_write_locked_target(
 
3405
                self.target, self._push_with_bound_branches, overwrite,
 
3406
                stop_revision, 
 
3407
                _override_hook_source_branch=_override_hook_source_branch)
 
3408
        finally:
 
3409
            self.source.unlock()
3332
3410
 
3333
3411
    def _basic_push(self, overwrite, stop_revision):
3334
3412
        """Basic implementation of push without bound branches or hooks.
3340
3418
        result.target_branch = self.target
3341
3419
        result.old_revno, result.old_revid = self.target.last_revision_info()
3342
3420
        self.source.update_references(self.target)
3343
 
        overwrite = _fix_overwrite_type(overwrite)
3344
3421
        if result.old_revid != stop_revision:
3345
3422
            # We assume that during 'push' this repository is closer than
3346
3423
            # the target.
3347
3424
            graph = self.source.repository.get_graph(self.target.repository)
3348
 
            self._update_revisions(stop_revision,
3349
 
                overwrite=("history" in overwrite),
3350
 
                graph=graph)
 
3425
            self._update_revisions(stop_revision, overwrite=overwrite,
 
3426
                    graph=graph)
3351
3427
        if self.source._push_should_merge_tags():
3352
 
            result.tag_updates, result.tag_conflicts = (
3353
 
                self.source.tags.merge_to(
3354
 
                self.target.tags, "tags" in overwrite))
 
3428
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3429
                overwrite)
3355
3430
        result.new_revno, result.new_revid = self.target.last_revision_info()
3356
3431
        return result
3357
3432
 
3358
 
    def _push_with_bound_branches(self, operation, overwrite, stop_revision,
 
3433
    def _push_with_bound_branches(self, overwrite, stop_revision,
3359
3434
            _override_hook_source_branch=None):
3360
3435
        """Push from source into target, and into target's master if any.
3361
3436
        """
3373
3448
            # be bound to itself? -- mbp 20070507
3374
3449
            master_branch = self.target.get_master_branch()
3375
3450
            master_branch.lock_write()
3376
 
            operation.add_cleanup(master_branch.unlock)
3377
 
            # push into the master from the source branch.
3378
 
            master_inter = InterBranch.get(self.source, master_branch)
3379
 
            master_inter._basic_push(overwrite, stop_revision)
3380
 
            # and push into the target branch from the source. Note that
3381
 
            # we push from the source branch again, because it's considered
3382
 
            # the highest bandwidth repository.
3383
 
            result = self._basic_push(overwrite, stop_revision)
3384
 
            result.master_branch = master_branch
3385
 
            result.local_branch = self.target
 
3451
            try:
 
3452
                # push into the master from the source branch.
 
3453
                master_inter = InterBranch.get(self.source, master_branch)
 
3454
                master_inter._basic_push(overwrite, stop_revision)
 
3455
                # and push into the target branch from the source. Note that
 
3456
                # we push from the source branch again, because it's considered
 
3457
                # the highest bandwidth repository.
 
3458
                result = self._basic_push(overwrite, stop_revision)
 
3459
                result.master_branch = master_branch
 
3460
                result.local_branch = self.target
 
3461
                _run_hooks()
 
3462
                return result
 
3463
            finally:
 
3464
                master_branch.unlock()
3386
3465
        else:
3387
 
            master_branch = None
3388
3466
            # no master branch
3389
3467
            result = self._basic_push(overwrite, stop_revision)
3390
3468
            # TODO: Why set master_branch and local_branch if there's no
3392
3470
            # 20070504
3393
3471
            result.master_branch = self.target
3394
3472
            result.local_branch = None
3395
 
        _run_hooks()
3396
 
        return result
 
3473
            _run_hooks()
 
3474
            return result
3397
3475
 
3398
3476
    def _pull(self, overwrite=False, stop_revision=None,
3399
3477
             possible_transports=None, _hook_master=None, run_hooks=True,
3435
3513
            # -- JRV20090506
3436
3514
            result.old_revno, result.old_revid = \
3437
3515
                self.target.last_revision_info()
3438
 
            overwrite = _fix_overwrite_type(overwrite)
3439
 
            self._update_revisions(stop_revision,
3440
 
                overwrite=("history" in overwrite),
 
3516
            self._update_revisions(stop_revision, overwrite=overwrite,
3441
3517
                graph=graph)
3442
3518
            # TODO: The old revid should be specified when merging tags, 
3443
3519
            # so a tags implementation that versions tags can only 
3444
3520
            # pull in the most recent changes. -- JRV20090506
3445
 
            result.tag_updates, result.tag_conflicts = (
3446
 
                self.source.tags.merge_to(self.target.tags,
3447
 
                    "tags" in overwrite,
3448
 
                    ignore_master=not merge_tags_to_master))
 
3521
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3522
                overwrite, ignore_master=not merge_tags_to_master)
3449
3523
            result.new_revno, result.new_revid = self.target.last_revision_info()
3450
3524
            if _hook_master:
3451
3525
                result.master_branch = _hook_master