/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/branch.py

  • Committer: Jelmer Vernooij
  • Date: 2017-05-22 00:56:52 UTC
  • mfrom: (6621.2.26 py3_pokes)
  • Revision ID: jelmer@jelmer.uk-20170522005652-yjahcr9hwmjkno7n
Merge Python3 porting work ('py3 pokes')

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2012 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
 
 
18
 
from cStringIO import StringIO
19
 
import sys
20
 
 
21
 
from bzrlib.lazy_import import lazy_import
 
17
from __future__ import absolute_import
 
18
 
 
19
import breezy.bzrdir
 
20
 
 
21
from .lazy_import import lazy_import
22
22
lazy_import(globals(), """
23
 
from itertools import chain
24
 
from bzrlib import (
25
 
        bzrdir,
26
 
        cache_utf8,
27
 
        config as _mod_config,
28
 
        debug,
29
 
        errors,
30
 
        lockdir,
31
 
        lockable_files,
32
 
        repository,
33
 
        revision as _mod_revision,
34
 
        rio,
35
 
        symbol_versioning,
36
 
        transport,
37
 
        tsort,
38
 
        ui,
39
 
        urlutils,
40
 
        )
41
 
from bzrlib.config import BranchConfig, TransportConfig
42
 
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
43
 
from bzrlib.tag import (
44
 
    BasicTags,
45
 
    DisabledTags,
 
23
import itertools
 
24
from breezy import (
 
25
    bzrdir,
 
26
    controldir,
 
27
    cache_utf8,
 
28
    cleanup,
 
29
    config as _mod_config,
 
30
    debug,
 
31
    errors,
 
32
    fetch,
 
33
    graph as _mod_graph,
 
34
    lockdir,
 
35
    lockable_files,
 
36
    remote,
 
37
    repository,
 
38
    revision as _mod_revision,
 
39
    rio,
 
40
    shelf,
 
41
    tag as _mod_tag,
 
42
    transport,
 
43
    ui,
 
44
    urlutils,
 
45
    vf_search,
46
46
    )
 
47
from breezy.i18n import gettext, ngettext
47
48
""")
48
49
 
49
 
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
50
 
from bzrlib.hooks import HookPoint, Hooks
51
 
from bzrlib.inter import InterObject
52
 
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
53
 
from bzrlib import registry
54
 
from bzrlib.symbol_versioning import (
 
50
# Explicitly import breezy.bzrdir so that the BzrProber
 
51
# is guaranteed to be registered.
 
52
import breezy.bzrdir
 
53
 
 
54
from . import (
 
55
    bzrdir,
 
56
    controldir,
 
57
    registry,
 
58
    )
 
59
from .decorators import (
 
60
    needs_read_lock,
 
61
    needs_write_lock,
 
62
    only_raises,
 
63
    )
 
64
from .hooks import Hooks
 
65
from .inter import InterObject
 
66
from .lock import _RelockDebugMixin, LogicalLockResult
 
67
from .sixish import (
 
68
    BytesIO,
 
69
    )
 
70
from .symbol_versioning import (
55
71
    deprecated_in,
56
72
    deprecated_method,
57
73
    )
58
 
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
59
 
 
60
 
 
61
 
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
62
 
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
63
 
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
64
 
 
65
 
 
66
 
class Branch(bzrdir.ControlComponent):
 
74
from .trace import mutter, mutter_callsite, note, is_quiet
 
75
 
 
76
 
 
77
class Branch(controldir.ControlComponent):
67
78
    """Branch holding a history of revisions.
68
79
 
69
80
    :ivar base:
70
81
        Base directory/url of the branch; using control_url and
71
82
        control_transport is more standardized.
72
 
 
73
 
    hooks: An instance of BranchHooks.
 
83
    :ivar hooks: An instance of BranchHooks.
 
84
    :ivar _master_branch_cache: cached result of get_master_branch, see
 
85
        _clear_cached_state.
74
86
    """
75
87
    # this is really an instance variable - FIXME move it there
76
88
    # - RBC 20060112
84
96
    def user_transport(self):
85
97
        return self.bzrdir.user_transport
86
98
 
87
 
    def __init__(self, *ignored, **ignored_too):
 
99
    def __init__(self, possible_transports=None):
88
100
        self.tags = self._format.make_tags(self)
89
101
        self._revision_history_cache = None
90
102
        self._revision_id_to_revno_cache = None
91
103
        self._partial_revision_id_to_revno_cache = {}
92
104
        self._partial_revision_history_cache = []
 
105
        self._tags_bytes = None
93
106
        self._last_revision_info_cache = None
 
107
        self._master_branch_cache = None
94
108
        self._merge_sorted_revisions_cache = None
95
 
        self._open_hook()
 
109
        self._open_hook(possible_transports)
96
110
        hooks = Branch.hooks['open']
97
111
        for hook in hooks:
98
112
            hook(self)
99
113
 
100
 
    def _open_hook(self):
 
114
    def _open_hook(self, possible_transports):
101
115
        """Called by init to allow simpler extension of the base class."""
102
116
 
103
 
    def _activate_fallback_location(self, url):
 
117
    def _activate_fallback_location(self, url, possible_transports):
104
118
        """Activate the branch/repository from url as a fallback repository."""
105
 
        repo = self._get_fallback_repository(url)
 
119
        for existing_fallback_repo in self.repository._fallback_repositories:
 
120
            if existing_fallback_repo.user_url == url:
 
121
                # This fallback is already configured.  This probably only
 
122
                # happens because ControlDir.sprout is a horrible mess.  To avoid
 
123
                # confusing _unstack we don't add this a second time.
 
124
                mutter('duplicate activation of fallback %r on %r', url, self)
 
125
                return
 
126
        repo = self._get_fallback_repository(url, possible_transports)
106
127
        if repo.has_same_location(self.repository):
107
128
            raise errors.UnstackableLocationError(self.user_url, url)
108
129
        self.repository.add_fallback_repository(repo)
150
171
    def _get_check_refs(self):
151
172
        """Get the references needed for check().
152
173
 
153
 
        See bzrlib.check.
 
174
        See breezy.check.
154
175
        """
155
176
        revid = self.last_revision()
156
177
        return [('revision-existence', revid), ('lefthand-distance', revid)]
162
183
        For instance, if the branch is at URL/.bzr/branch,
163
184
        Branch.open(URL) -> a Branch instance.
164
185
        """
165
 
        control = bzrdir.BzrDir.open(base, _unsupported,
166
 
                                     possible_transports=possible_transports)
167
 
        return control.open_branch(unsupported=_unsupported)
 
186
        control = controldir.ControlDir.open(base,
 
187
            possible_transports=possible_transports, _unsupported=_unsupported)
 
188
        return control.open_branch(unsupported=_unsupported,
 
189
            possible_transports=possible_transports)
168
190
 
169
191
    @staticmethod
170
 
    def open_from_transport(transport, name=None, _unsupported=False):
 
192
    def open_from_transport(transport, name=None, _unsupported=False,
 
193
            possible_transports=None):
171
194
        """Open the branch rooted at transport"""
172
 
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
173
 
        return control.open_branch(name=name, unsupported=_unsupported)
 
195
        control = controldir.ControlDir.open_from_transport(transport, _unsupported)
 
196
        return control.open_branch(name=name, unsupported=_unsupported,
 
197
            possible_transports=possible_transports)
174
198
 
175
199
    @staticmethod
176
200
    def open_containing(url, possible_transports=None):
184
208
        format, UnknownFormatError or UnsupportedFormatError are raised.
185
209
        If there is one, it is returned, along with the unused portion of url.
186
210
        """
187
 
        control, relpath = bzrdir.BzrDir.open_containing(url,
 
211
        control, relpath = controldir.ControlDir.open_containing(url,
188
212
                                                         possible_transports)
189
 
        return control.open_branch(), relpath
 
213
        branch = control.open_branch(possible_transports=possible_transports)
 
214
        return (branch, relpath)
190
215
 
191
216
    def _push_should_merge_tags(self):
192
217
        """Should _basic_push merge this branch's tags into the target?
197
222
        return self.supports_tags() and self.tags.get_tag_dict()
198
223
 
199
224
    def get_config(self):
200
 
        return BranchConfig(self)
 
225
        """Get a breezy.config.BranchConfig for this Branch.
 
226
 
 
227
        This can then be used to get and set configuration options for the
 
228
        branch.
 
229
 
 
230
        :return: A breezy.config.BranchConfig.
 
231
        """
 
232
        return _mod_config.BranchConfig(self)
 
233
 
 
234
    def get_config_stack(self):
 
235
        """Get a breezy.config.BranchStack for this Branch.
 
236
 
 
237
        This can then be used to get and set configuration options for the
 
238
        branch.
 
239
 
 
240
        :return: A breezy.config.BranchStack.
 
241
        """
 
242
        return _mod_config.BranchStack(self)
201
243
 
202
244
    def _get_config(self):
203
245
        """Get the concrete config for just the config in this branch.
211
253
        """
212
254
        raise NotImplementedError(self._get_config)
213
255
 
214
 
    def _get_fallback_repository(self, url):
 
256
    def store_uncommitted(self, creator):
 
257
        """Store uncommitted changes from a ShelfCreator.
 
258
 
 
259
        :param creator: The ShelfCreator containing uncommitted changes, or
 
260
            None to delete any stored changes.
 
261
        :raises: ChangesAlreadyStored if the branch already has changes.
 
262
        """
 
263
        raise NotImplementedError(self.store_uncommitted)
 
264
 
 
265
    def get_unshelver(self, tree):
 
266
        """Return a shelf.Unshelver for this branch and tree.
 
267
 
 
268
        :param tree: The tree to use to construct the Unshelver.
 
269
        :return: an Unshelver or None if no changes are stored.
 
270
        """
 
271
        raise NotImplementedError(self.get_unshelver)
 
272
 
 
273
    def _get_fallback_repository(self, url, possible_transports):
215
274
        """Get the repository we fallback to at url."""
216
275
        url = urlutils.join(self.base, url)
217
 
        a_branch = Branch.open(url,
218
 
            possible_transports=[self.bzrdir.root_transport])
 
276
        a_branch = Branch.open(url, possible_transports=possible_transports)
219
277
        return a_branch.repository
220
278
 
 
279
    @needs_read_lock
221
280
    def _get_tags_bytes(self):
222
281
        """Get the bytes of a serialised tags dict.
223
282
 
230
289
        :return: The bytes of the tags file.
231
290
        :seealso: Branch._set_tags_bytes.
232
291
        """
233
 
        return self._transport.get_bytes('tags')
 
292
        if self._tags_bytes is None:
 
293
            self._tags_bytes = self._transport.get_bytes('tags')
 
294
        return self._tags_bytes
234
295
 
235
296
    def _get_nick(self, local=False, possible_transports=None):
236
297
        config = self.get_config()
238
299
        if not local and not config.has_explicit_nickname():
239
300
            try:
240
301
                master = self.get_master_branch(possible_transports)
 
302
                if master and self.user_url == master.user_url:
 
303
                    raise errors.RecursiveBind(self.user_url)
241
304
                if master is not None:
242
305
                    # return the master branch value
243
306
                    return master.nick
244
 
            except errors.BzrError, e:
 
307
            except errors.RecursiveBind as e:
 
308
                raise e
 
309
            except errors.BzrError as e:
245
310
                # Silently fall back to local implicit nick if the master is
246
311
                # unavailable
247
312
                mutter("Could not connect to bound branch, "
295
360
    def lock_read(self):
296
361
        """Lock the branch for read operations.
297
362
 
298
 
        :return: A bzrlib.lock.LogicalLockResult.
 
363
        :return: A breezy.lock.LogicalLockResult.
299
364
        """
300
365
        raise NotImplementedError(self.lock_read)
301
366
 
423
488
            after. If None, the rest of history is included.
424
489
        :param stop_rule: if stop_revision_id is not None, the precise rule
425
490
            to use for termination:
 
491
 
426
492
            * 'exclude' - leave the stop revision out of the result (default)
427
493
            * 'include' - the stop revision is the last item in the result
428
494
            * 'with-merges' - include the stop revision and all of its
430
496
            * 'with-merges-without-common-ancestry' - filter out revisions 
431
497
              that are in both ancestries
432
498
        :param direction: either 'reverse' or 'forward':
 
499
 
433
500
            * reverse means return the start_revision_id first, i.e.
434
501
              start at the most recent revision and go backwards in history
435
502
            * forward returns tuples in the opposite order to reverse.
479
546
        rev_iter = iter(merge_sorted_revisions)
480
547
        if start_revision_id is not None:
481
548
            for node in rev_iter:
482
 
                rev_id = node.key[-1]
 
549
                rev_id = node.key
483
550
                if rev_id != start_revision_id:
484
551
                    continue
485
552
                else:
486
553
                    # The decision to include the start or not
487
554
                    # depends on the stop_rule if a stop is provided
488
555
                    # so pop this node back into the iterator
489
 
                    rev_iter = chain(iter([node]), rev_iter)
 
556
                    rev_iter = itertools.chain(iter([node]), rev_iter)
490
557
                    break
491
558
        if stop_revision_id is None:
492
559
            # Yield everything
493
560
            for node in rev_iter:
494
 
                rev_id = node.key[-1]
 
561
                rev_id = node.key
495
562
                yield (rev_id, node.merge_depth, node.revno,
496
563
                       node.end_of_merge)
497
564
        elif stop_rule == 'exclude':
498
565
            for node in rev_iter:
499
 
                rev_id = node.key[-1]
 
566
                rev_id = node.key
500
567
                if rev_id == stop_revision_id:
501
568
                    return
502
569
                yield (rev_id, node.merge_depth, node.revno,
503
570
                       node.end_of_merge)
504
571
        elif stop_rule == 'include':
505
572
            for node in rev_iter:
506
 
                rev_id = node.key[-1]
 
573
                rev_id = node.key
507
574
                yield (rev_id, node.merge_depth, node.revno,
508
575
                       node.end_of_merge)
509
576
                if rev_id == stop_revision_id:
515
582
            ancestors = graph.find_unique_ancestors(start_revision_id,
516
583
                                                    [stop_revision_id])
517
584
            for node in rev_iter:
518
 
                rev_id = node.key[-1]
 
585
                rev_id = node.key
519
586
                if rev_id not in ancestors:
520
587
                    continue
521
588
                yield (rev_id, node.merge_depth, node.revno,
531
598
            reached_stop_revision_id = False
532
599
            revision_id_whitelist = []
533
600
            for node in rev_iter:
534
 
                rev_id = node.key[-1]
 
601
                rev_id = node.key
535
602
                if rev_id == left_parent:
536
603
                    # reached the left parent after the stop_revision
537
604
                    return
617
684
        """
618
685
        raise errors.UpgradeRequired(self.user_url)
619
686
 
 
687
    def get_append_revisions_only(self):
 
688
        """Whether it is only possible to append revisions to the history.
 
689
        """
 
690
        if not self._format.supports_set_append_revisions_only():
 
691
            return False
 
692
        return self.get_config_stack().get('append_revisions_only')
 
693
 
620
694
    def set_append_revisions_only(self, enabled):
621
695
        if not self._format.supports_set_append_revisions_only():
622
696
            raise errors.UpgradeRequired(self.user_url)
623
 
        if enabled:
624
 
            value = 'True'
625
 
        else:
626
 
            value = 'False'
627
 
        self.get_config().set_user_option('append_revisions_only', value,
628
 
            warn_masked=True)
 
697
        self.get_config_stack().set('append_revisions_only', enabled)
629
698
 
630
699
    def set_reference_info(self, file_id, tree_path, branch_location):
631
700
        """Set the branch location to use for a tree reference."""
636
705
        raise errors.UnsupportedOperation(self.get_reference_info, self)
637
706
 
638
707
    @needs_write_lock
639
 
    def fetch(self, from_branch, last_revision=None, pb=None):
 
708
    def fetch(self, from_branch, last_revision=None, limit=None):
640
709
        """Copy revisions from from_branch into this branch.
641
710
 
642
711
        :param from_branch: Where to copy from.
643
712
        :param last_revision: What revision to stop at (None for at the end
644
713
                              of the branch.
645
 
        :param pb: An optional progress bar to use.
 
714
        :param limit: Optional rough limit of revisions to fetch
646
715
        :return: None
647
716
        """
648
 
        if self.base == from_branch.base:
649
 
            return (0, [])
650
 
        if pb is not None:
651
 
            symbol_versioning.warn(
652
 
                symbol_versioning.deprecated_in((1, 14, 0))
653
 
                % "pb parameter to fetch()")
654
 
        from_branch.lock_read()
655
 
        try:
656
 
            if last_revision is None:
657
 
                last_revision = from_branch.last_revision()
658
 
                last_revision = _mod_revision.ensure_null(last_revision)
659
 
            return self.repository.fetch(from_branch.repository,
660
 
                                         revision_id=last_revision,
661
 
                                         pb=pb)
662
 
        finally:
663
 
            from_branch.unlock()
 
717
        return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
664
718
 
665
719
    def get_bound_location(self):
666
720
        """Return the URL of the branch we are bound to.
675
729
        """
676
730
        raise errors.UpgradeRequired(self.user_url)
677
731
 
678
 
    def get_commit_builder(self, parents, config=None, timestamp=None,
 
732
    def get_commit_builder(self, parents, config_stack=None, timestamp=None,
679
733
                           timezone=None, committer=None, revprops=None,
680
 
                           revision_id=None):
 
734
                           revision_id=None, lossy=False):
681
735
        """Obtain a CommitBuilder for this branch.
682
736
 
683
737
        :param parents: Revision ids of the parents of the new revision.
687
741
        :param committer: Optional committer to set for commit.
688
742
        :param revprops: Optional dictionary of revision properties.
689
743
        :param revision_id: Optional revision id.
 
744
        :param lossy: Whether to discard data that can not be natively
 
745
            represented, when pushing to a foreign VCS 
690
746
        """
691
747
 
692
 
        if config is None:
693
 
            config = self.get_config()
 
748
        if config_stack is None:
 
749
            config_stack = self.get_config_stack()
694
750
 
695
 
        return self.repository.get_commit_builder(self, parents, config,
696
 
            timestamp, timezone, committer, revprops, revision_id)
 
751
        return self.repository.get_commit_builder(self, parents, config_stack,
 
752
            timestamp, timezone, committer, revprops, revision_id,
 
753
            lossy)
697
754
 
698
755
    def get_master_branch(self, possible_transports=None):
699
756
        """Return the branch we are bound to.
702
759
        """
703
760
        return None
704
761
 
 
762
    @deprecated_method(deprecated_in((2, 5, 0)))
705
763
    def get_revision_delta(self, revno):
706
764
        """Return the delta for one revision.
707
765
 
708
766
        The delta is relative to its mainline predecessor, or the
709
767
        empty tree for revision 1.
710
768
        """
711
 
        rh = self.revision_history()
712
 
        if not (1 <= revno <= len(rh)):
 
769
        try:
 
770
            revid = self.get_rev_id(revno)
 
771
        except errors.NoSuchRevision:
713
772
            raise errors.InvalidRevisionNumber(revno)
714
 
        return self.repository.get_revision_delta(rh[revno-1])
 
773
        return self.repository.get_revision_delta(revid)
715
774
 
716
775
    def get_stacked_on_url(self):
717
776
        """Get the URL this branch is stacked against.
726
785
        """Print `file` to stdout."""
727
786
        raise NotImplementedError(self.print_file)
728
787
 
729
 
    def set_revision_history(self, rev_history):
730
 
        raise NotImplementedError(self.set_revision_history)
 
788
    @needs_write_lock
 
789
    def set_last_revision_info(self, revno, revision_id):
 
790
        """Set the last revision of this branch.
 
791
 
 
792
        The caller is responsible for checking that the revno is correct
 
793
        for this revision id.
 
794
 
 
795
        It may be possible to set the branch last revision to an id not
 
796
        present in the repository.  However, branches can also be
 
797
        configured to check constraints on history, in which case this may not
 
798
        be permitted.
 
799
        """
 
800
        raise NotImplementedError(self.set_last_revision_info)
 
801
 
 
802
    @needs_write_lock
 
803
    def generate_revision_history(self, revision_id, last_rev=None,
 
804
                                  other_branch=None):
 
805
        """See Branch.generate_revision_history"""
 
806
        graph = self.repository.get_graph()
 
807
        (last_revno, last_revid) = self.last_revision_info()
 
808
        known_revision_ids = [
 
809
            (last_revid, last_revno),
 
810
            (_mod_revision.NULL_REVISION, 0),
 
811
            ]
 
812
        if last_rev is not None:
 
813
            if not graph.is_ancestor(last_rev, revision_id):
 
814
                # our previous tip is not merged into stop_revision
 
815
                raise errors.DivergedBranches(self, other_branch)
 
816
        revno = graph.find_distance_to_null(revision_id, known_revision_ids)
 
817
        self.set_last_revision_info(revno, revision_id)
731
818
 
732
819
    @needs_write_lock
733
820
    def set_parent(self, url):
743
830
                except UnicodeEncodeError:
744
831
                    raise errors.InvalidURL(url,
745
832
                        "Urls must be 7-bit ascii, "
746
 
                        "use bzrlib.urlutils.escape")
 
833
                        "use breezy.urlutils.escape")
747
834
            url = urlutils.relative_url(self.base, url)
748
835
        self._set_parent_location(url)
749
836
 
770
857
                return
771
858
            self._unstack()
772
859
        else:
773
 
            self._activate_fallback_location(url)
 
860
            self._activate_fallback_location(url,
 
861
                possible_transports=[self.bzrdir.root_transport])
774
862
        # write this out after the repository is stacked to avoid setting a
775
863
        # stacked config that doesn't work.
776
864
        self._set_config_location('stacked_on_location', url)
777
865
 
778
866
    def _unstack(self):
779
867
        """Change a branch to be unstacked, copying data as needed.
780
 
        
 
868
 
781
869
        Don't call this directly, use set_stacked_on_url(None).
782
870
        """
783
871
        pb = ui.ui_factory.nested_progress_bar()
784
872
        try:
785
 
            pb.update("Unstacking")
 
873
            pb.update(gettext("Unstacking"))
786
874
            # The basic approach here is to fetch the tip of the branch,
787
875
            # including all available ghosts, from the existing stacked
788
876
            # repository into a new repository object without the fallbacks. 
792
880
            old_repository = self.repository
793
881
            if len(old_repository._fallback_repositories) != 1:
794
882
                raise AssertionError("can't cope with fallback repositories "
795
 
                    "of %r" % (self.repository,))
796
 
            # unlock it, including unlocking the fallback
 
883
                    "of %r (fallbacks: %r)" % (old_repository,
 
884
                        old_repository._fallback_repositories))
 
885
            # Open the new repository object.
 
886
            # Repositories don't offer an interface to remove fallback
 
887
            # repositories today; take the conceptually simpler option and just
 
888
            # reopen it.  We reopen it starting from the URL so that we
 
889
            # get a separate connection for RemoteRepositories and can
 
890
            # stream from one of them to the other.  This does mean doing
 
891
            # separate SSH connection setup, but unstacking is not a
 
892
            # common operation so it's tolerable.
 
893
            new_bzrdir = controldir.ControlDir.open(
 
894
                self.bzrdir.root_transport.base)
 
895
            new_repository = new_bzrdir.find_repository()
 
896
            if new_repository._fallback_repositories:
 
897
                raise AssertionError("didn't expect %r to have "
 
898
                    "fallback_repositories"
 
899
                    % (self.repository,))
 
900
            # Replace self.repository with the new repository.
 
901
            # Do our best to transfer the lock state (i.e. lock-tokens and
 
902
            # lock count) of self.repository to the new repository.
 
903
            lock_token = old_repository.lock_write().repository_token
 
904
            self.repository = new_repository
 
905
            if isinstance(self, remote.RemoteBranch):
 
906
                # Remote branches can have a second reference to the old
 
907
                # repository that need to be replaced.
 
908
                if self._real_branch is not None:
 
909
                    self._real_branch.repository = new_repository
 
910
            self.repository.lock_write(token=lock_token)
 
911
            if lock_token is not None:
 
912
                old_repository.leave_lock_in_place()
797
913
            old_repository.unlock()
 
914
            if lock_token is not None:
 
915
                # XXX: self.repository.leave_lock_in_place() before this
 
916
                # function will not be preserved.  Fortunately that doesn't
 
917
                # affect the current default format (2a), and would be a
 
918
                # corner-case anyway.
 
919
                #  - Andrew Bennetts, 2010/06/30
 
920
                self.repository.dont_leave_lock_in_place()
 
921
            old_lock_count = 0
 
922
            while True:
 
923
                try:
 
924
                    old_repository.unlock()
 
925
                except errors.LockNotHeld:
 
926
                    break
 
927
                old_lock_count += 1
 
928
            if old_lock_count == 0:
 
929
                raise AssertionError(
 
930
                    'old_repository should have been locked at least once.')
 
931
            for i in range(old_lock_count-1):
 
932
                self.repository.lock_write()
 
933
            # Fetch from the old repository into the new.
798
934
            old_repository.lock_read()
799
935
            try:
800
 
                # Repositories don't offer an interface to remove fallback
801
 
                # repositories today; take the conceptually simpler option and just
802
 
                # reopen it.  We reopen it starting from the URL so that we
803
 
                # get a separate connection for RemoteRepositories and can
804
 
                # stream from one of them to the other.  This does mean doing
805
 
                # separate SSH connection setup, but unstacking is not a
806
 
                # common operation so it's tolerable.
807
 
                new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
808
 
                new_repository = new_bzrdir.find_repository()
809
 
                self.repository = new_repository
810
 
                if self.repository._fallback_repositories:
811
 
                    raise AssertionError("didn't expect %r to have "
812
 
                        "fallback_repositories"
813
 
                        % (self.repository,))
814
 
                # this is not paired with an unlock because it's just restoring
815
 
                # the previous state; the lock's released when set_stacked_on_url
816
 
                # returns
817
 
                self.repository.lock_write()
818
936
                # XXX: If you unstack a branch while it has a working tree
819
937
                # with a pending merge, the pending-merged revisions will no
820
938
                # longer be present.  You can (probably) revert and remerge.
821
 
                #
822
 
                # XXX: This only fetches up to the tip of the repository; it
823
 
                # doesn't bring across any tags.  That's fairly consistent
824
 
                # with how branch works, but perhaps not ideal.
825
 
                self.repository.fetch(old_repository,
826
 
                    revision_id=self.last_revision(),
827
 
                    find_ghosts=True)
 
939
                try:
 
940
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
 
941
                except errors.TagsNotSupported:
 
942
                    tags_to_fetch = set()
 
943
                fetch_spec = vf_search.NotInOtherForRevs(self.repository,
 
944
                    old_repository, required_ids=[self.last_revision()],
 
945
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
 
946
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
828
947
            finally:
829
948
                old_repository.unlock()
830
949
        finally:
835
954
 
836
955
        :seealso: Branch._get_tags_bytes.
837
956
        """
838
 
        return _run_with_write_locked_target(self, self._transport.put_bytes,
839
 
            'tags', bytes)
 
957
        op = cleanup.OperationWithCleanups(self._set_tags_bytes_locked)
 
958
        op.add_cleanup(self.lock_write().unlock)
 
959
        return op.run_simple(bytes)
 
960
 
 
961
    def _set_tags_bytes_locked(self, bytes):
 
962
        self._tags_bytes = bytes
 
963
        return self._transport.put_bytes('tags', bytes)
840
964
 
841
965
    def _cache_revision_history(self, rev_history):
842
966
        """Set the cached revision history to rev_history.
863
987
        This means the next call to revision_history will need to call
864
988
        _gen_revision_history.
865
989
 
866
 
        This API is semi-public; it only for use by subclasses, all other code
867
 
        should consider it to be private.
 
990
        This API is semi-public; it is only for use by subclasses, all other
 
991
        code should consider it to be private.
868
992
        """
869
993
        self._revision_history_cache = None
870
994
        self._revision_id_to_revno_cache = None
871
995
        self._last_revision_info_cache = None
 
996
        self._master_branch_cache = None
872
997
        self._merge_sorted_revisions_cache = None
873
998
        self._partial_revision_history_cache = []
874
999
        self._partial_revision_id_to_revno_cache = {}
 
1000
        self._tags_bytes = None
875
1001
 
876
1002
    def _gen_revision_history(self):
877
1003
        """Return sequence of revision hashes on to this branch.
888
1014
        """
889
1015
        raise NotImplementedError(self._gen_revision_history)
890
1016
 
891
 
    @needs_read_lock
892
 
    def revision_history(self):
893
 
        """Return sequence of revision ids on this branch.
894
 
 
895
 
        This method will cache the revision history for as long as it is safe to
896
 
        do so.
897
 
        """
 
1017
    def _revision_history(self):
898
1018
        if 'evil' in debug.debug_flags:
899
1019
            mutter_callsite(3, "revision_history scales with history.")
900
1020
        if self._revision_history_cache is not None:
927
1047
        :return: A tuple (revno, revision_id).
928
1048
        """
929
1049
        if self._last_revision_info_cache is None:
930
 
            self._last_revision_info_cache = self._last_revision_info()
 
1050
            self._last_revision_info_cache = self._read_last_revision_info()
931
1051
        return self._last_revision_info_cache
932
1052
 
933
 
    def _last_revision_info(self):
934
 
        rh = self.revision_history()
935
 
        revno = len(rh)
936
 
        if revno:
937
 
            return (revno, rh[-1])
938
 
        else:
939
 
            return (0, _mod_revision.NULL_REVISION)
940
 
 
941
 
    @deprecated_method(deprecated_in((1, 6, 0)))
942
 
    def missing_revisions(self, other, stop_revision=None):
943
 
        """Return a list of new revisions that would perfectly fit.
944
 
 
945
 
        If self and other have not diverged, return a list of the revisions
946
 
        present in other, but missing from self.
947
 
        """
948
 
        self_history = self.revision_history()
949
 
        self_len = len(self_history)
950
 
        other_history = other.revision_history()
951
 
        other_len = len(other_history)
952
 
        common_index = min(self_len, other_len) -1
953
 
        if common_index >= 0 and \
954
 
            self_history[common_index] != other_history[common_index]:
955
 
            raise errors.DivergedBranches(self, other)
956
 
 
957
 
        if stop_revision is None:
958
 
            stop_revision = other_len
959
 
        else:
960
 
            if stop_revision > other_len:
961
 
                raise errors.NoSuchRevision(self, stop_revision)
962
 
        return other_history[self_len:stop_revision]
963
 
 
964
 
    @needs_write_lock
965
 
    def update_revisions(self, other, stop_revision=None, overwrite=False,
966
 
                         graph=None):
967
 
        """Pull in new perfect-fit revisions.
968
 
 
969
 
        :param other: Another Branch to pull from
970
 
        :param stop_revision: Updated until the given revision
971
 
        :param overwrite: Always set the branch pointer, rather than checking
972
 
            to see if it is a proper descendant.
973
 
        :param graph: A Graph object that can be used to query history
974
 
            information. This can be None.
975
 
        :return: None
976
 
        """
977
 
        return InterBranch.get(other, self).update_revisions(stop_revision,
978
 
            overwrite, graph)
979
 
 
980
 
    def import_last_revision_info(self, source_repo, revno, revid):
 
1053
    def _read_last_revision_info(self):
 
1054
        raise NotImplementedError(self._read_last_revision_info)
 
1055
 
 
1056
    def import_last_revision_info_and_tags(self, source, revno, revid,
 
1057
                                           lossy=False):
981
1058
        """Set the last revision info, importing from another repo if necessary.
982
1059
 
983
1060
        This is used by the bound branch code to upload a revision to
984
1061
        the master branch first before updating the tip of the local branch.
 
1062
        Revisions referenced by source's tags are also transferred.
985
1063
 
986
 
        :param source_repo: Source repository to optionally fetch from
 
1064
        :param source: Source branch to optionally fetch from
987
1065
        :param revno: Revision number of the new tip
988
1066
        :param revid: Revision id of the new tip
 
1067
        :param lossy: Whether to discard metadata that can not be
 
1068
            natively represented
 
1069
        :return: Tuple with the new revision number and revision id
 
1070
            (should only be different from the arguments when lossy=True)
989
1071
        """
990
 
        if not self.repository.has_same_location(source_repo):
991
 
            self.repository.fetch(source_repo, revision_id=revid)
 
1072
        if not self.repository.has_same_location(source.repository):
 
1073
            self.fetch(source, revid)
992
1074
        self.set_last_revision_info(revno, revid)
 
1075
        return (revno, revid)
993
1076
 
994
1077
    def revision_id_to_revno(self, revision_id):
995
1078
        """Given a revision id, return its revno"""
996
1079
        if _mod_revision.is_null(revision_id):
997
1080
            return 0
998
 
        history = self.revision_history()
 
1081
        history = self._revision_history()
999
1082
        try:
1000
1083
            return history.index(revision_id) + 1
1001
1084
        except ValueError:
1016
1099
            self._extend_partial_history(distance_from_last)
1017
1100
        return self._partial_revision_history_cache[distance_from_last]
1018
1101
 
1019
 
    @needs_write_lock
1020
1102
    def pull(self, source, overwrite=False, stop_revision=None,
1021
1103
             possible_transports=None, *args, **kwargs):
1022
1104
        """Mirror source into this branch.
1029
1111
            stop_revision=stop_revision,
1030
1112
            possible_transports=possible_transports, *args, **kwargs)
1031
1113
 
1032
 
    def push(self, target, overwrite=False, stop_revision=None, *args,
1033
 
        **kwargs):
 
1114
    def push(self, target, overwrite=False, stop_revision=None, lossy=False,
 
1115
            *args, **kwargs):
1034
1116
        """Mirror this branch into target.
1035
1117
 
1036
1118
        This branch is considered to be 'local', having low latency.
1037
1119
        """
1038
1120
        return InterBranch.get(self, target).push(overwrite, stop_revision,
1039
 
            *args, **kwargs)
1040
 
 
1041
 
    def lossy_push(self, target, stop_revision=None):
1042
 
        """Push deltas into another branch.
1043
 
 
1044
 
        :note: This does not, like push, retain the revision ids from 
1045
 
            the source branch and will, rather than adding bzr-specific 
1046
 
            metadata, push only those semantics of the revision that can be 
1047
 
            natively represented by this branch' VCS.
1048
 
 
1049
 
        :param target: Target branch
1050
 
        :param stop_revision: Revision to push, defaults to last revision.
1051
 
        :return: BranchPushResult with an extra member revidmap: 
1052
 
            A dictionary mapping revision ids from the target branch 
1053
 
            to new revision ids in the target branch, for each 
1054
 
            revision that was pushed.
1055
 
        """
1056
 
        inter = InterBranch.get(self, target)
1057
 
        lossy_push = getattr(inter, "lossy_push", None)
1058
 
        if lossy_push is None:
1059
 
            raise errors.LossyPushToSameVCS(self, target)
1060
 
        return lossy_push(stop_revision)
 
1121
            lossy, *args, **kwargs)
1061
1122
 
1062
1123
    def basis_tree(self):
1063
1124
        """Return `Tree` object for last revision."""
1079
1140
            parent = urlutils.local_path_to_url(parent.decode('utf8'))
1080
1141
        try:
1081
1142
            return urlutils.join(self.base[:-1], parent)
1082
 
        except errors.InvalidURLJoin, e:
 
1143
        except errors.InvalidURLJoin as e:
1083
1144
            raise errors.InaccessibleParent(parent, self.user_url)
1084
1145
 
1085
1146
    def _get_parent_location(self):
1088
1149
    def _set_config_location(self, name, url, config=None,
1089
1150
                             make_relative=False):
1090
1151
        if config is None:
1091
 
            config = self.get_config()
 
1152
            config = self.get_config_stack()
1092
1153
        if url is None:
1093
1154
            url = ''
1094
1155
        elif make_relative:
1095
1156
            url = urlutils.relative_url(self.base, url)
1096
 
        config.set_user_option(name, url, warn_masked=True)
 
1157
        config.set(name, url)
1097
1158
 
1098
1159
    def _get_config_location(self, name, config=None):
1099
1160
        if config is None:
1100
 
            config = self.get_config()
1101
 
        location = config.get_user_option(name)
 
1161
            config = self.get_config_stack()
 
1162
        location = config.get(name)
1102
1163
        if location == '':
1103
1164
            location = None
1104
1165
        return location
1105
1166
 
1106
1167
    def get_child_submit_format(self):
1107
1168
        """Return the preferred format of submissions to this branch."""
1108
 
        return self.get_config().get_user_option("child_submit_format")
 
1169
        return self.get_config_stack().get('child_submit_format')
1109
1170
 
1110
1171
    def get_submit_branch(self):
1111
1172
        """Return the submit location of the branch.
1114
1175
        pattern is that the user can override it by specifying a
1115
1176
        location.
1116
1177
        """
1117
 
        return self.get_config().get_user_option('submit_branch')
 
1178
        return self.get_config_stack().get('submit_branch')
1118
1179
 
1119
1180
    def set_submit_branch(self, location):
1120
1181
        """Return the submit location of the branch.
1123
1184
        pattern is that the user can override it by specifying a
1124
1185
        location.
1125
1186
        """
1126
 
        self.get_config().set_user_option('submit_branch', location,
1127
 
            warn_masked=True)
 
1187
        self.get_config_stack().set('submit_branch', location)
1128
1188
 
1129
1189
    def get_public_branch(self):
1130
1190
        """Return the public location of the branch.
1143
1203
        self._set_config_location('public_branch', location)
1144
1204
 
1145
1205
    def get_push_location(self):
1146
 
        """Return the None or the location to push this branch to."""
1147
 
        push_loc = self.get_config().get_user_option('push_location')
1148
 
        return push_loc
 
1206
        """Return None or the location to push this branch to."""
 
1207
        return self.get_config_stack().get('push_location')
1149
1208
 
1150
1209
    def set_push_location(self, location):
1151
1210
        """Set a new push location for this branch."""
1218
1277
        return result
1219
1278
 
1220
1279
    @needs_read_lock
1221
 
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
 
1280
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
 
1281
            repository=None):
1222
1282
        """Create a new line of development from the branch, into to_bzrdir.
1223
1283
 
1224
1284
        to_bzrdir controls the branch format.
1229
1289
        if (repository_policy is not None and
1230
1290
            repository_policy.requires_stacking()):
1231
1291
            to_bzrdir._format.require_stacking(_skip_repo=True)
1232
 
        result = to_bzrdir.create_branch()
 
1292
        result = to_bzrdir.create_branch(repository=repository)
1233
1293
        result.lock_write()
1234
1294
        try:
1235
1295
            if repository_policy is not None:
1236
1296
                repository_policy.configure_branch(result)
1237
1297
            self.copy_content_into(result, revision_id=revision_id)
1238
 
            result.set_parent(self.bzrdir.root_transport.base)
 
1298
            master_url = self.get_bound_location()
 
1299
            if master_url is None:
 
1300
                result.set_parent(self.bzrdir.root_transport.base)
 
1301
            else:
 
1302
                result.set_parent(master_url)
1239
1303
        finally:
1240
1304
            result.unlock()
1241
1305
        return result
1265
1329
                revno = 1
1266
1330
        destination.set_last_revision_info(revno, revision_id)
1267
1331
 
1268
 
    @needs_read_lock
1269
1332
    def copy_content_into(self, destination, revision_id=None):
1270
1333
        """Copy the content of self into destination.
1271
1334
 
1272
1335
        revision_id: if not None, the revision history in the new branch will
1273
1336
                     be truncated to end with revision_id.
1274
1337
        """
1275
 
        self.update_references(destination)
1276
 
        self._synchronize_history(destination, revision_id)
1277
 
        try:
1278
 
            parent = self.get_parent()
1279
 
        except errors.InaccessibleParent, e:
1280
 
            mutter('parent was not accessible to copy: %s', e)
1281
 
        else:
1282
 
            if parent:
1283
 
                destination.set_parent(parent)
1284
 
        if self._push_should_merge_tags():
1285
 
            self.tags.merge_to(destination.tags)
 
1338
        return InterBranch.get(self, destination).copy_content_into(
 
1339
            revision_id=revision_id)
1286
1340
 
1287
1341
    def update_references(self, target):
1288
1342
        if not getattr(self._format, 'supports_reference_locations', False):
1325
1379
        # TODO: We should probably also check that self.revision_history
1326
1380
        # matches the repository for older branch formats.
1327
1381
        # If looking for the code that cross-checks repository parents against
1328
 
        # the iter_reverse_revision_history output, that is now a repository
 
1382
        # the Graph.iter_lefthand_ancestry output, that is now a repository
1329
1383
        # specific check.
1330
1384
        return result
1331
1385
 
1332
 
    def _get_checkout_format(self):
 
1386
    def _get_checkout_format(self, lightweight=False):
1333
1387
        """Return the most suitable metadir for a checkout of this branch.
1334
1388
        Weaves are used if this branch's repository uses weaves.
1335
1389
        """
1336
 
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
1337
 
            from bzrlib.repofmt import weaverepo
1338
 
            format = bzrdir.BzrDirMetaFormat1()
1339
 
            format.repository_format = weaverepo.RepositoryFormat7()
1340
 
        else:
1341
 
            format = self.repository.bzrdir.checkout_metadir()
1342
 
            format.set_branch_format(self._format)
 
1390
        format = self.repository.bzrdir.checkout_metadir()
 
1391
        format.set_branch_format(self._format)
1343
1392
        return format
1344
1393
 
1345
1394
    def create_clone_on_transport(self, to_transport, revision_id=None,
1346
 
        stacked_on=None, create_prefix=False, use_existing_dir=False):
 
1395
        stacked_on=None, create_prefix=False, use_existing_dir=False,
 
1396
        no_tree=None):
1347
1397
        """Create a clone of this branch and its bzrdir.
1348
1398
 
1349
1399
        :param to_transport: The transport to clone onto.
1362
1412
            revision_id = self.last_revision()
1363
1413
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1364
1414
            revision_id=revision_id, stacked_on=stacked_on,
1365
 
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
 
1415
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
 
1416
            no_tree=no_tree)
1366
1417
        return dir_to.open_branch()
1367
1418
 
1368
1419
    def create_checkout(self, to_location, revision_id=None,
1373
1424
        :param to_location: The url to produce the checkout at
1374
1425
        :param revision_id: The revision to check out
1375
1426
        :param lightweight: If True, produce a lightweight checkout, otherwise,
1376
 
        produce a bound branch (heavyweight checkout)
 
1427
            produce a bound branch (heavyweight checkout)
1377
1428
        :param accelerator_tree: A tree which can be used for retrieving file
1378
1429
            contents more quickly than the revision tree, i.e. a workingtree.
1379
1430
            The revision tree will be used for cases where accelerator_tree's
1384
1435
        """
1385
1436
        t = transport.get_transport(to_location)
1386
1437
        t.ensure_base()
 
1438
        format = self._get_checkout_format(lightweight=lightweight)
 
1439
        try:
 
1440
            checkout = format.initialize_on_transport(t)
 
1441
        except errors.AlreadyControlDirError:
 
1442
            # It's fine if the control directory already exists,
 
1443
            # as long as there is no existing branch and working tree.
 
1444
            checkout = controldir.ControlDir.open_from_transport(t)
 
1445
            try:
 
1446
                checkout.open_branch()
 
1447
            except errors.NotBranchError:
 
1448
                pass
 
1449
            else:
 
1450
                raise errors.AlreadyControlDirError(t.base)
 
1451
            if checkout.control_transport.base == self.bzrdir.control_transport.base:
 
1452
                # When checking out to the same control directory,
 
1453
                # always create a lightweight checkout
 
1454
                lightweight = True
 
1455
 
1387
1456
        if lightweight:
1388
 
            format = self._get_checkout_format()
1389
 
            checkout = format.initialize_on_transport(t)
1390
 
            from_branch = BranchReferenceFormat().initialize(checkout, 
1391
 
                target_branch=self)
 
1457
            from_branch = checkout.set_branch_reference(target_branch=self)
1392
1458
        else:
1393
 
            format = self._get_checkout_format()
1394
 
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1395
 
                to_location, force_new_tree=False, format=format)
1396
 
            checkout = checkout_branch.bzrdir
 
1459
            policy = checkout.determine_repository_policy()
 
1460
            repo = policy.acquire_repository()[0]
 
1461
            checkout_branch = checkout.create_branch()
1397
1462
            checkout_branch.bind(self)
1398
1463
            # pull up to the specified revision_id to set the initial
1399
1464
            # branch tip correctly, and seed it with history.
1400
1465
            checkout_branch.pull(self, stop_revision=revision_id)
1401
 
            from_branch=None
 
1466
            from_branch = None
1402
1467
        tree = checkout.create_workingtree(revision_id,
1403
1468
                                           from_branch=from_branch,
1404
1469
                                           accelerator_tree=accelerator_tree,
1418
1483
    @needs_write_lock
1419
1484
    def reconcile(self, thorough=True):
1420
1485
        """Make sure the data stored in this branch is consistent."""
1421
 
        from bzrlib.reconcile import BranchReconciler
 
1486
        from breezy.reconcile import BranchReconciler
1422
1487
        reconciler = BranchReconciler(self, thorough=thorough)
1423
1488
        reconciler.reconcile()
1424
1489
        return reconciler
1425
1490
 
1426
1491
    def reference_parent(self, file_id, path, possible_transports=None):
1427
1492
        """Return the parent branch for a tree-reference file_id
 
1493
 
1428
1494
        :param file_id: The file_id of the tree reference
1429
1495
        :param path: The path of the file_id in the tree
1430
1496
        :return: A branch associated with the file_id
1473
1539
        :returns: One of: 'a_descends_from_b', 'b_descends_from_a', 'diverged'
1474
1540
        """
1475
1541
        heads = graph.heads([revision_a, revision_b])
1476
 
        if heads == set([revision_b]):
 
1542
        if heads == {revision_b}:
1477
1543
            return 'b_descends_from_a'
1478
 
        elif heads == set([revision_a, revision_b]):
 
1544
        elif heads == {revision_a, revision_b}:
1479
1545
            # These branches have diverged
1480
1546
            return 'diverged'
1481
 
        elif heads == set([revision_a]):
 
1547
        elif heads == {revision_a}:
1482
1548
            return 'a_descends_from_b'
1483
1549
        else:
1484
1550
            raise AssertionError("invalid heads: %r" % (heads,))
1485
1551
 
1486
 
 
1487
 
class BranchFormat(object):
 
1552
    def heads_to_fetch(self):
 
1553
        """Return the heads that must and that should be fetched to copy this
 
1554
        branch into another repo.
 
1555
 
 
1556
        :returns: a 2-tuple of (must_fetch, if_present_fetch).  must_fetch is a
 
1557
            set of heads that must be fetched.  if_present_fetch is a set of
 
1558
            heads that must be fetched if present, but no error is necessary if
 
1559
            they are not present.
 
1560
        """
 
1561
        # For bzr native formats must_fetch is just the tip, and
 
1562
        # if_present_fetch are the tags.
 
1563
        must_fetch = {self.last_revision()}
 
1564
        if_present_fetch = set()
 
1565
        if self.get_config_stack().get('branch.fetch_tags'):
 
1566
            try:
 
1567
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
 
1568
            except errors.TagsNotSupported:
 
1569
                pass
 
1570
        must_fetch.discard(_mod_revision.NULL_REVISION)
 
1571
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
 
1572
        return must_fetch, if_present_fetch
 
1573
 
 
1574
 
 
1575
class BranchFormat(controldir.ControlComponentFormat):
1488
1576
    """An encapsulation of the initialization and open routines for a format.
1489
1577
 
1490
1578
    Formats provide three things:
1491
1579
     * An initialization routine,
1492
 
     * a format string,
 
1580
     * a format description
1493
1581
     * an open routine.
1494
1582
 
1495
1583
    Formats are placed in an dict by their format string for reference
1496
 
    during branch opening. Its not required that these be instances, they
 
1584
    during branch opening. It's not required that these be instances, they
1497
1585
    can be classes themselves with class methods - it simply depends on
1498
1586
    whether state is needed for a given format or not.
1499
1587
 
1502
1590
    object will be created every time regardless.
1503
1591
    """
1504
1592
 
1505
 
    _default_format = None
1506
 
    """The default format used for new branches."""
1507
 
 
1508
 
    _formats = {}
1509
 
    """The known formats."""
1510
 
 
1511
 
    can_set_append_revisions_only = True
1512
 
 
1513
1593
    def __eq__(self, other):
1514
1594
        return self.__class__ is other.__class__
1515
1595
 
1516
1596
    def __ne__(self, other):
1517
1597
        return not (self == other)
1518
1598
 
1519
 
    @classmethod
1520
 
    def find_format(klass, a_bzrdir, name=None):
1521
 
        """Return the format for the branch object in a_bzrdir."""
1522
 
        try:
1523
 
            transport = a_bzrdir.get_branch_transport(None, name=name)
1524
 
            format_string = transport.get_bytes("format")
1525
 
            return klass._formats[format_string]
1526
 
        except errors.NoSuchFile:
1527
 
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1528
 
        except KeyError:
1529
 
            raise errors.UnknownFormatError(format=format_string, kind='branch')
1530
 
 
1531
 
    @classmethod
1532
 
    def get_default_format(klass):
1533
 
        """Return the current default format."""
1534
 
        return klass._default_format
1535
 
 
1536
 
    def get_reference(self, a_bzrdir, name=None):
1537
 
        """Get the target reference of the branch in a_bzrdir.
 
1599
    def get_reference(self, controldir, name=None):
 
1600
        """Get the target reference of the branch in controldir.
1538
1601
 
1539
1602
        format probing must have been completed before calling
1540
1603
        this method - it is assumed that the format of the branch
1541
 
        in a_bzrdir is correct.
 
1604
        in controldir is correct.
1542
1605
 
1543
 
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1606
        :param controldir: The controldir to get the branch data from.
1544
1607
        :param name: Name of the colocated branch to fetch
1545
1608
        :return: None if the branch is not a reference branch.
1546
1609
        """
1547
1610
        return None
1548
1611
 
1549
1612
    @classmethod
1550
 
    def set_reference(self, a_bzrdir, name, to_branch):
1551
 
        """Set the target reference of the branch in a_bzrdir.
 
1613
    def set_reference(self, controldir, name, to_branch):
 
1614
        """Set the target reference of the branch in controldir.
1552
1615
 
1553
1616
        format probing must have been completed before calling
1554
1617
        this method - it is assumed that the format of the branch
1555
 
        in a_bzrdir is correct.
 
1618
        in controldir is correct.
1556
1619
 
1557
 
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1620
        :param controldir: The controldir to set the branch reference for.
1558
1621
        :param name: Name of colocated branch to set, None for default
1559
1622
        :param to_branch: branch that the checkout is to reference
1560
1623
        """
1561
1624
        raise NotImplementedError(self.set_reference)
1562
1625
 
1563
 
    def get_format_string(self):
1564
 
        """Return the ASCII format string that identifies this format."""
1565
 
        raise NotImplementedError(self.get_format_string)
1566
 
 
1567
1626
    def get_format_description(self):
1568
1627
        """Return the short format description for this format."""
1569
1628
        raise NotImplementedError(self.get_format_description)
1570
1629
 
1571
 
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
 
1630
    def _run_post_branch_init_hooks(self, controldir, name, branch):
1572
1631
        hooks = Branch.hooks['post_branch_init']
1573
1632
        if not hooks:
1574
1633
            return
1575
 
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
 
1634
        params = BranchInitHookParams(self, controldir, name, branch)
1576
1635
        for hook in hooks:
1577
1636
            hook(params)
1578
1637
 
1579
 
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1580
 
                           lock_type='metadir', set_format=True):
1581
 
        """Initialize a branch in a bzrdir, with specified files
1582
 
 
1583
 
        :param a_bzrdir: The bzrdir to initialize the branch in
1584
 
        :param utf8_files: The files to create as a list of
1585
 
            (filename, content) tuples
1586
 
        :param name: Name of colocated branch to create, if any
1587
 
        :param set_format: If True, set the format with
1588
 
            self.get_format_string.  (BzrBranch4 has its format set
1589
 
            elsewhere)
1590
 
        :return: a branch in this format
1591
 
        """
1592
 
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1593
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1594
 
        lock_map = {
1595
 
            'metadir': ('lock', lockdir.LockDir),
1596
 
            'branch4': ('branch-lock', lockable_files.TransportLock),
1597
 
        }
1598
 
        lock_name, lock_class = lock_map[lock_type]
1599
 
        control_files = lockable_files.LockableFiles(branch_transport,
1600
 
            lock_name, lock_class)
1601
 
        control_files.create_lock()
1602
 
        try:
1603
 
            control_files.lock_write()
1604
 
        except errors.LockContention:
1605
 
            if lock_type != 'branch4':
1606
 
                raise
1607
 
            lock_taken = False
1608
 
        else:
1609
 
            lock_taken = True
1610
 
        if set_format:
1611
 
            utf8_files += [('format', self.get_format_string())]
1612
 
        try:
1613
 
            for (filename, content) in utf8_files:
1614
 
                branch_transport.put_bytes(
1615
 
                    filename, content,
1616
 
                    mode=a_bzrdir._get_file_mode())
1617
 
        finally:
1618
 
            if lock_taken:
1619
 
                control_files.unlock()
1620
 
        branch = self.open(a_bzrdir, name, _found=True)
1621
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1622
 
        return branch
1623
 
 
1624
 
    def initialize(self, a_bzrdir, name=None):
1625
 
        """Create a branch of this format in a_bzrdir.
1626
 
        
 
1638
    def initialize(self, controldir, name=None, repository=None,
 
1639
                   append_revisions_only=None):
 
1640
        """Create a branch of this format in controldir.
 
1641
 
1627
1642
        :param name: Name of the colocated branch to create.
1628
1643
        """
1629
1644
        raise NotImplementedError(self.initialize)
1649
1664
        Note that it is normal for branch to be a RemoteBranch when using tags
1650
1665
        on a RemoteBranch.
1651
1666
        """
1652
 
        return DisabledTags(branch)
 
1667
        return _mod_tag.DisabledTags(branch)
1653
1668
 
1654
1669
    def network_name(self):
1655
1670
        """A simple byte string uniquely identifying this format for RPC calls.
1661
1676
        """
1662
1677
        raise NotImplementedError(self.network_name)
1663
1678
 
1664
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1665
 
        """Return the branch object for a_bzrdir
 
1679
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
 
1680
            found_repository=None, possible_transports=None):
 
1681
        """Return the branch object for controldir.
1666
1682
 
1667
 
        :param a_bzrdir: A BzrDir that contains a branch.
 
1683
        :param controldir: A ControlDir that contains a branch.
1668
1684
        :param name: Name of colocated branch to open
1669
1685
        :param _found: a private parameter, do not use it. It is used to
1670
1686
            indicate if format probing has already be done.
1673
1689
        """
1674
1690
        raise NotImplementedError(self.open)
1675
1691
 
1676
 
    @classmethod
1677
 
    def register_format(klass, format):
1678
 
        """Register a metadir format."""
1679
 
        klass._formats[format.get_format_string()] = format
1680
 
        # Metadir formats have a network name of their format string, and get
1681
 
        # registered as class factories.
1682
 
        network_format_registry.register(format.get_format_string(), format.__class__)
1683
 
 
1684
 
    @classmethod
1685
 
    def set_default_format(klass, format):
1686
 
        klass._default_format = format
1687
 
 
1688
1692
    def supports_set_append_revisions_only(self):
1689
1693
        """True if this format supports set_append_revisions_only."""
1690
1694
        return False
1693
1697
        """True if this format records a stacked-on branch."""
1694
1698
        return False
1695
1699
 
1696
 
    @classmethod
1697
 
    def unregister_format(klass, format):
1698
 
        del klass._formats[format.get_format_string()]
 
1700
    def supports_leaving_lock(self):
 
1701
        """True if this format supports leaving locks in place."""
 
1702
        return False # by default
1699
1703
 
1700
1704
    def __str__(self):
1701
1705
        return self.get_format_description().rstrip()
1704
1708
        """True if this format supports tags stored in the branch"""
1705
1709
        return False  # by default
1706
1710
 
 
1711
    def tags_are_versioned(self):
 
1712
        """Whether the tag container for this branch versions tags."""
 
1713
        return False
 
1714
 
 
1715
    def supports_tags_referencing_ghosts(self):
 
1716
        """True if tags can reference ghost revisions."""
 
1717
        return True
 
1718
 
 
1719
 
 
1720
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
 
1721
    """A factory for a BranchFormat object, permitting simple lazy registration.
 
1722
    
 
1723
    While none of the built in BranchFormats are lazy registered yet,
 
1724
    breezy.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
 
1725
    use it, and the bzr-loom plugin uses it as well (see
 
1726
    breezy.plugins.loom.formats).
 
1727
    """
 
1728
 
 
1729
    def __init__(self, format_string, module_name, member_name):
 
1730
        """Create a MetaDirBranchFormatFactory.
 
1731
 
 
1732
        :param format_string: The format string the format has.
 
1733
        :param module_name: Module to load the format class from.
 
1734
        :param member_name: Attribute name within the module for the format class.
 
1735
        """
 
1736
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
 
1737
        self._format_string = format_string
 
1738
 
 
1739
    def get_format_string(self):
 
1740
        """See BranchFormat.get_format_string."""
 
1741
        return self._format_string
 
1742
 
 
1743
    def __call__(self):
 
1744
        """Used for network_format_registry support."""
 
1745
        return self.get_obj()()
 
1746
 
1707
1747
 
1708
1748
class BranchHooks(Hooks):
1709
1749
    """A dictionary mapping hook name to a list of callables for branch hooks.
1710
1750
 
1711
 
    e.g. ['set_rh'] Is the list of items to be called when the
1712
 
    set_revision_history function is invoked.
 
1751
    e.g. ['post_push'] Is the list of items to be called when the
 
1752
    push function is invoked.
1713
1753
    """
1714
1754
 
1715
1755
    def __init__(self):
1718
1758
        These are all empty initially, because by default nothing should get
1719
1759
        notified.
1720
1760
        """
1721
 
        Hooks.__init__(self)
1722
 
        self.create_hook(HookPoint('set_rh',
1723
 
            "Invoked whenever the revision history has been set via "
1724
 
            "set_revision_history. The api signature is (branch, "
1725
 
            "revision_history), and the branch will be write-locked. "
1726
 
            "The set_rh hook can be expensive for bzr to trigger, a better "
1727
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1728
 
        self.create_hook(HookPoint('open',
 
1761
        Hooks.__init__(self, "breezy.branch", "Branch.hooks")
 
1762
        self.add_hook('open',
1729
1763
            "Called with the Branch object that has been opened after a "
1730
 
            "branch is opened.", (1, 8), None))
1731
 
        self.create_hook(HookPoint('post_push',
 
1764
            "branch is opened.", (1, 8))
 
1765
        self.add_hook('post_push',
1732
1766
            "Called after a push operation completes. post_push is called "
1733
 
            "with a bzrlib.branch.BranchPushResult object and only runs in the "
1734
 
            "bzr client.", (0, 15), None))
1735
 
        self.create_hook(HookPoint('post_pull',
 
1767
            "with a breezy.branch.BranchPushResult object and only runs in the "
 
1768
            "bzr client.", (0, 15))
 
1769
        self.add_hook('post_pull',
1736
1770
            "Called after a pull operation completes. post_pull is called "
1737
 
            "with a bzrlib.branch.PullResult object and only runs in the "
1738
 
            "bzr client.", (0, 15), None))
1739
 
        self.create_hook(HookPoint('pre_commit',
1740
 
            "Called after a commit is calculated but before it is is "
 
1771
            "with a breezy.branch.PullResult object and only runs in the "
 
1772
            "bzr client.", (0, 15))
 
1773
        self.add_hook('pre_commit',
 
1774
            "Called after a commit is calculated but before it is "
1741
1775
            "completed. pre_commit is called with (local, master, old_revno, "
1742
1776
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1743
1777
            "). old_revid is NULL_REVISION for the first commit to a branch, "
1745
1779
            "basis revision. hooks MUST NOT modify this delta. "
1746
1780
            " future_tree is an in-memory tree obtained from "
1747
1781
            "CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1748
 
            "tree.", (0,91), None))
1749
 
        self.create_hook(HookPoint('post_commit',
 
1782
            "tree.", (0,91))
 
1783
        self.add_hook('post_commit',
1750
1784
            "Called in the bzr client after a commit has completed. "
1751
1785
            "post_commit is called with (local, master, old_revno, old_revid, "
1752
1786
            "new_revno, new_revid). old_revid is NULL_REVISION for the first "
1753
 
            "commit to a branch.", (0, 15), None))
1754
 
        self.create_hook(HookPoint('post_uncommit',
 
1787
            "commit to a branch.", (0, 15))
 
1788
        self.add_hook('post_uncommit',
1755
1789
            "Called in the bzr client after an uncommit completes. "
1756
1790
            "post_uncommit is called with (local, master, old_revno, "
1757
1791
            "old_revid, new_revno, new_revid) where local is the local branch "
1758
1792
            "or None, master is the target branch, and an empty branch "
1759
 
            "receives new_revno of 0, new_revid of None.", (0, 15), None))
1760
 
        self.create_hook(HookPoint('pre_change_branch_tip',
 
1793
            "receives new_revno of 0, new_revid of None.", (0, 15))
 
1794
        self.add_hook('pre_change_branch_tip',
1761
1795
            "Called in bzr client and server before a change to the tip of a "
1762
1796
            "branch is made. pre_change_branch_tip is called with a "
1763
 
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1764
 
            "commit, uncommit will all trigger this hook.", (1, 6), None))
1765
 
        self.create_hook(HookPoint('post_change_branch_tip',
 
1797
            "breezy.branch.ChangeBranchTipParams. Note that push, pull, "
 
1798
            "commit, uncommit will all trigger this hook.", (1, 6))
 
1799
        self.add_hook('post_change_branch_tip',
1766
1800
            "Called in bzr client and server after a change to the tip of a "
1767
1801
            "branch is made. post_change_branch_tip is called with a "
1768
 
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1769
 
            "commit, uncommit will all trigger this hook.", (1, 4), None))
1770
 
        self.create_hook(HookPoint('transform_fallback_location',
 
1802
            "breezy.branch.ChangeBranchTipParams. Note that push, pull, "
 
1803
            "commit, uncommit will all trigger this hook.", (1, 4))
 
1804
        self.add_hook('transform_fallback_location',
1771
1805
            "Called when a stacked branch is activating its fallback "
1772
1806
            "locations. transform_fallback_location is called with (branch, "
1773
1807
            "url), and should return a new url. Returning the same url "
1778
1812
            "fallback locations have not been activated. When there are "
1779
1813
            "multiple hooks installed for transform_fallback_location, "
1780
1814
            "all are called with the url returned from the previous hook."
1781
 
            "The order is however undefined.", (1, 9), None))
1782
 
        self.create_hook(HookPoint('automatic_tag_name',
1783
 
            "Called to determine an automatic tag name for a revision."
 
1815
            "The order is however undefined.", (1, 9))
 
1816
        self.add_hook('automatic_tag_name',
 
1817
            "Called to determine an automatic tag name for a revision. "
1784
1818
            "automatic_tag_name is called with (branch, revision_id) and "
1785
1819
            "should return a tag name or None if no tag name could be "
1786
1820
            "determined. The first non-None tag name returned will be used.",
1787
 
            (2, 2), None))
1788
 
        self.create_hook(HookPoint('post_branch_init',
 
1821
            (2, 2))
 
1822
        self.add_hook('post_branch_init',
1789
1823
            "Called after new branch initialization completes. "
1790
1824
            "post_branch_init is called with a "
1791
 
            "bzrlib.branch.BranchInitHookParams. "
 
1825
            "breezy.branch.BranchInitHookParams. "
1792
1826
            "Note that init, branch and checkout (both heavyweight and "
1793
 
            "lightweight) will all trigger this hook.", (2, 2), None))
1794
 
        self.create_hook(HookPoint('post_switch',
 
1827
            "lightweight) will all trigger this hook.", (2, 2))
 
1828
        self.add_hook('post_switch',
1795
1829
            "Called after a checkout switches branch. "
1796
1830
            "post_switch is called with a "
1797
 
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
 
1831
            "breezy.branch.SwitchHookParams.", (2, 2))
1798
1832
 
1799
1833
 
1800
1834
 
1803
1837
 
1804
1838
 
1805
1839
class ChangeBranchTipParams(object):
1806
 
    """Object holding parameters passed to *_change_branch_tip hooks.
 
1840
    """Object holding parameters passed to `*_change_branch_tip` hooks.
1807
1841
 
1808
1842
    There are 5 fields that hooks may wish to access:
1809
1843
 
1841
1875
 
1842
1876
 
1843
1877
class BranchInitHookParams(object):
1844
 
    """Object holding parameters passed to *_branch_init hooks.
 
1878
    """Object holding parameters passed to `*_branch_init` hooks.
1845
1879
 
1846
1880
    There are 4 fields that hooks may wish to access:
1847
1881
 
1848
1882
    :ivar format: the branch format
1849
 
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
 
1883
    :ivar bzrdir: the ControlDir where the branch will be/has been initialized
1850
1884
    :ivar name: name of colocated branch, if any (or None)
1851
1885
    :ivar branch: the branch created
1852
1886
 
1855
1889
    branch, which refer to the original branch.
1856
1890
    """
1857
1891
 
1858
 
    def __init__(self, format, a_bzrdir, name, branch):
 
1892
    def __init__(self, format, controldir, name, branch):
1859
1893
        """Create a group of BranchInitHook parameters.
1860
1894
 
1861
1895
        :param format: the branch format
1862
 
        :param a_bzrdir: the BzrDir where the branch will be/has been
 
1896
        :param controldir: the ControlDir where the branch will be/has been
1863
1897
            initialized
1864
1898
        :param name: name of colocated branch, if any (or None)
1865
1899
        :param branch: the branch created
1869
1903
        in branch, which refer to the original branch.
1870
1904
        """
1871
1905
        self.format = format
1872
 
        self.bzrdir = a_bzrdir
 
1906
        self.bzrdir = controldir
1873
1907
        self.name = name
1874
1908
        self.branch = branch
1875
1909
 
1877
1911
        return self.__dict__ == other.__dict__
1878
1912
 
1879
1913
    def __repr__(self):
1880
 
        if self.branch:
1881
 
            return "<%s of %s>" % (self.__class__.__name__, self.branch)
1882
 
        else:
1883
 
            return "<%s of format:%s bzrdir:%s>" % (
1884
 
                self.__class__.__name__, self.branch,
1885
 
                self.format, self.bzrdir)
 
1914
        return "<%s of %s>" % (self.__class__.__name__, self.branch)
1886
1915
 
1887
1916
 
1888
1917
class SwitchHookParams(object):
1889
 
    """Object holding parameters passed to *_switch hooks.
 
1918
    """Object holding parameters passed to `*_switch` hooks.
1890
1919
 
1891
1920
    There are 4 fields that hooks may wish to access:
1892
1921
 
1893
 
    :ivar control_dir: BzrDir of the checkout to change
 
1922
    :ivar control_dir: ControlDir of the checkout to change
1894
1923
    :ivar to_branch: branch that the checkout is to reference
1895
1924
    :ivar force: skip the check for local commits in a heavy checkout
1896
1925
    :ivar revision_id: revision ID to switch to (or None)
1899
1928
    def __init__(self, control_dir, to_branch, force, revision_id):
1900
1929
        """Create a group of SwitchHook parameters.
1901
1930
 
1902
 
        :param control_dir: BzrDir of the checkout to change
 
1931
        :param control_dir: ControlDir of the checkout to change
1903
1932
        :param to_branch: branch that the checkout is to reference
1904
1933
        :param force: skip the check for local commits in a heavy checkout
1905
1934
        :param revision_id: revision ID to switch to (or None)
1918
1947
            self.revision_id)
1919
1948
 
1920
1949
 
1921
 
class BzrBranchFormat4(BranchFormat):
1922
 
    """Bzr branch format 4.
1923
 
 
1924
 
    This format has:
1925
 
     - a revision-history file.
1926
 
     - a branch-lock lock file [ to be shared with the bzrdir ]
 
1950
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
 
1951
    """Base class for branch formats that live in meta directories.
1927
1952
    """
1928
1953
 
1929
 
    def get_format_description(self):
1930
 
        """See BranchFormat.get_format_description()."""
1931
 
        return "Branch format 4"
1932
 
 
1933
 
    def initialize(self, a_bzrdir, name=None):
1934
 
        """Create a branch of this format in a_bzrdir."""
1935
 
        utf8_files = [('revision-history', ''),
1936
 
                      ('branch-name', ''),
1937
 
                      ]
1938
 
        return self._initialize_helper(a_bzrdir, utf8_files, name=name,
1939
 
                                       lock_type='branch4', set_format=False)
1940
 
 
1941
1954
    def __init__(self):
1942
 
        super(BzrBranchFormat4, self).__init__()
1943
 
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
1944
 
 
1945
 
    def network_name(self):
1946
 
        """The network name for this format is the control dirs disk label."""
1947
 
        return self._matchingbzrdir.get_format_string()
1948
 
 
1949
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1950
 
        """See BranchFormat.open()."""
1951
 
        if not _found:
1952
 
            # we are being called directly and must probe.
1953
 
            raise NotImplementedError
1954
 
        return BzrBranch(_format=self,
1955
 
                         _control_files=a_bzrdir._control_files,
1956
 
                         a_bzrdir=a_bzrdir,
1957
 
                         name=name,
1958
 
                         _repository=a_bzrdir.open_repository())
1959
 
 
1960
 
    def __str__(self):
1961
 
        return "Bazaar-NG branch format 4"
1962
 
 
1963
 
 
1964
 
class BranchFormatMetadir(BranchFormat):
1965
 
    """Common logic for meta-dir based branch formats."""
 
1955
        BranchFormat.__init__(self)
 
1956
        bzrdir.BzrFormat.__init__(self)
 
1957
 
 
1958
    @classmethod
 
1959
    def find_format(klass, controldir, name=None):
 
1960
        """Return the format for the branch object in controldir."""
 
1961
        try:
 
1962
            transport = controldir.get_branch_transport(None, name=name)
 
1963
        except errors.NoSuchFile:
 
1964
            raise errors.NotBranchError(path=name, bzrdir=controldir)
 
1965
        try:
 
1966
            format_string = transport.get_bytes("format")
 
1967
        except errors.NoSuchFile:
 
1968
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
 
1969
        return klass._find_format(format_registry, 'branch', format_string)
1966
1970
 
1967
1971
    def _branch_class(self):
1968
1972
        """What class to instantiate on open calls."""
1969
1973
        raise NotImplementedError(self._branch_class)
1970
1974
 
1971
 
    def network_name(self):
1972
 
        """A simple byte string uniquely identifying this format for RPC calls.
1973
 
 
1974
 
        Metadir branch formats use their format string.
 
1975
    def _get_initial_config(self, append_revisions_only=None):
 
1976
        if append_revisions_only:
 
1977
            return "append_revisions_only = True\n"
 
1978
        else:
 
1979
            # Avoid writing anything if append_revisions_only is disabled,
 
1980
            # as that is the default.
 
1981
            return ""
 
1982
 
 
1983
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
1984
                           repository=None):
 
1985
        """Initialize a branch in a control dir, with specified files
 
1986
 
 
1987
        :param a_bzrdir: The bzrdir to initialize the branch in
 
1988
        :param utf8_files: The files to create as a list of
 
1989
            (filename, content) tuples
 
1990
        :param name: Name of colocated branch to create, if any
 
1991
        :return: a branch in this format
1975
1992
        """
1976
 
        return self.get_format_string()
 
1993
        if name is None:
 
1994
            name = a_bzrdir._get_selected_branch()
 
1995
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
1996
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
 
1997
        control_files = lockable_files.LockableFiles(branch_transport,
 
1998
            'lock', lockdir.LockDir)
 
1999
        control_files.create_lock()
 
2000
        control_files.lock_write()
 
2001
        try:
 
2002
            utf8_files += [('format', self.as_string())]
 
2003
            for (filename, content) in utf8_files:
 
2004
                branch_transport.put_bytes(
 
2005
                    filename, content,
 
2006
                    mode=a_bzrdir._get_file_mode())
 
2007
        finally:
 
2008
            control_files.unlock()
 
2009
        branch = self.open(a_bzrdir, name, _found=True,
 
2010
                found_repository=repository)
 
2011
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
2012
        return branch
1977
2013
 
1978
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
2014
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
 
2015
            found_repository=None, possible_transports=None):
1979
2016
        """See BranchFormat.open()."""
 
2017
        if name is None:
 
2018
            name = a_bzrdir._get_selected_branch()
1980
2019
        if not _found:
1981
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2020
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
1982
2021
            if format.__class__ != self.__class__:
1983
2022
                raise AssertionError("wrong format %r found for %r" %
1984
2023
                    (format, self))
1986
2025
        try:
1987
2026
            control_files = lockable_files.LockableFiles(transport, 'lock',
1988
2027
                                                         lockdir.LockDir)
 
2028
            if found_repository is None:
 
2029
                found_repository = a_bzrdir.find_repository()
1989
2030
            return self._branch_class()(_format=self,
1990
2031
                              _control_files=control_files,
1991
2032
                              name=name,
1992
2033
                              a_bzrdir=a_bzrdir,
1993
 
                              _repository=a_bzrdir.find_repository(),
1994
 
                              ignore_fallbacks=ignore_fallbacks)
 
2034
                              _repository=found_repository,
 
2035
                              ignore_fallbacks=ignore_fallbacks,
 
2036
                              possible_transports=possible_transports)
1995
2037
        except errors.NoSuchFile:
1996
2038
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1997
2039
 
1998
 
    def __init__(self):
1999
 
        super(BranchFormatMetadir, self).__init__()
2000
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2001
 
        self._matchingbzrdir.set_branch_format(self)
2002
 
 
2003
 
    def supports_tags(self):
2004
 
        return True
2005
 
 
2006
 
 
2007
 
class BzrBranchFormat5(BranchFormatMetadir):
2008
 
    """Bzr branch format 5.
2009
 
 
2010
 
    This format has:
2011
 
     - a revision-history file.
2012
 
     - a format string
2013
 
     - a lock dir guarding the branch itself
2014
 
     - all of this stored in a branch/ subdirectory
2015
 
     - works with shared repositories.
2016
 
 
2017
 
    This format is new in bzr 0.8.
2018
 
    """
2019
 
 
2020
 
    def _branch_class(self):
2021
 
        return BzrBranch5
2022
 
 
2023
 
    def get_format_string(self):
2024
 
        """See BranchFormat.get_format_string()."""
2025
 
        return "Bazaar-NG branch format 5\n"
2026
 
 
2027
 
    def get_format_description(self):
2028
 
        """See BranchFormat.get_format_description()."""
2029
 
        return "Branch format 5"
2030
 
 
2031
 
    def initialize(self, a_bzrdir, name=None):
2032
 
        """Create a branch of this format in a_bzrdir."""
2033
 
        utf8_files = [('revision-history', ''),
2034
 
                      ('branch-name', ''),
2035
 
                      ]
2036
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2037
 
 
2038
 
    def supports_tags(self):
2039
 
        return False
 
2040
    @property
 
2041
    def _matchingbzrdir(self):
 
2042
        ret = bzrdir.BzrDirMetaFormat1()
 
2043
        ret.set_branch_format(self)
 
2044
        return ret
 
2045
 
 
2046
    def supports_tags(self):
 
2047
        return True
 
2048
 
 
2049
    def supports_leaving_lock(self):
 
2050
        return True
 
2051
 
 
2052
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
 
2053
            basedir=None):
 
2054
        BranchFormat.check_support_status(self,
 
2055
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
 
2056
            basedir=basedir)
 
2057
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
 
2058
            recommend_upgrade=recommend_upgrade, basedir=basedir)
2040
2059
 
2041
2060
 
2042
2061
class BzrBranchFormat6(BranchFormatMetadir):
2053
2072
    def _branch_class(self):
2054
2073
        return BzrBranch6
2055
2074
 
2056
 
    def get_format_string(self):
 
2075
    @classmethod
 
2076
    def get_format_string(cls):
2057
2077
        """See BranchFormat.get_format_string()."""
2058
2078
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
2059
2079
 
2061
2081
        """See BranchFormat.get_format_description()."""
2062
2082
        return "Branch format 6"
2063
2083
 
2064
 
    def initialize(self, a_bzrdir, name=None):
 
2084
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2085
                   append_revisions_only=None):
2065
2086
        """Create a branch of this format in a_bzrdir."""
2066
2087
        utf8_files = [('last-revision', '0 null:\n'),
2067
 
                      ('branch.conf', ''),
 
2088
                      ('branch.conf',
 
2089
                          self._get_initial_config(append_revisions_only)),
2068
2090
                      ('tags', ''),
2069
2091
                      ]
2070
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2092
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2071
2093
 
2072
2094
    def make_tags(self, branch):
2073
 
        """See bzrlib.branch.BranchFormat.make_tags()."""
2074
 
        return BasicTags(branch)
 
2095
        """See breezy.branch.BranchFormat.make_tags()."""
 
2096
        return _mod_tag.BasicTags(branch)
2075
2097
 
2076
2098
    def supports_set_append_revisions_only(self):
2077
2099
        return True
2083
2105
    def _branch_class(self):
2084
2106
        return BzrBranch8
2085
2107
 
2086
 
    def get_format_string(self):
 
2108
    @classmethod
 
2109
    def get_format_string(cls):
2087
2110
        """See BranchFormat.get_format_string()."""
2088
2111
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2089
2112
 
2091
2114
        """See BranchFormat.get_format_description()."""
2092
2115
        return "Branch format 8"
2093
2116
 
2094
 
    def initialize(self, a_bzrdir, name=None):
 
2117
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2118
                   append_revisions_only=None):
2095
2119
        """Create a branch of this format in a_bzrdir."""
2096
2120
        utf8_files = [('last-revision', '0 null:\n'),
2097
 
                      ('branch.conf', ''),
 
2121
                      ('branch.conf',
 
2122
                          self._get_initial_config(append_revisions_only)),
2098
2123
                      ('tags', ''),
2099
2124
                      ('references', '')
2100
2125
                      ]
2101
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2102
 
 
2103
 
    def __init__(self):
2104
 
        super(BzrBranchFormat8, self).__init__()
2105
 
        self._matchingbzrdir.repository_format = \
2106
 
            RepositoryFormatKnitPack5RichRoot()
 
2126
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2107
2127
 
2108
2128
    def make_tags(self, branch):
2109
 
        """See bzrlib.branch.BranchFormat.make_tags()."""
2110
 
        return BasicTags(branch)
 
2129
        """See breezy.branch.BranchFormat.make_tags()."""
 
2130
        return _mod_tag.BasicTags(branch)
2111
2131
 
2112
2132
    def supports_set_append_revisions_only(self):
2113
2133
        return True
2118
2138
    supports_reference_locations = True
2119
2139
 
2120
2140
 
2121
 
class BzrBranchFormat7(BzrBranchFormat8):
 
2141
class BzrBranchFormat7(BranchFormatMetadir):
2122
2142
    """Branch format with last-revision, tags, and a stacked location pointer.
2123
2143
 
2124
2144
    The stacked location pointer is passed down to the repository and requires
2127
2147
    This format was introduced in bzr 1.6.
2128
2148
    """
2129
2149
 
2130
 
    def initialize(self, a_bzrdir, name=None):
 
2150
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2151
                   append_revisions_only=None):
2131
2152
        """Create a branch of this format in a_bzrdir."""
2132
2153
        utf8_files = [('last-revision', '0 null:\n'),
2133
 
                      ('branch.conf', ''),
 
2154
                      ('branch.conf',
 
2155
                          self._get_initial_config(append_revisions_only)),
2134
2156
                      ('tags', ''),
2135
2157
                      ]
2136
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2158
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2137
2159
 
2138
2160
    def _branch_class(self):
2139
2161
        return BzrBranch7
2140
2162
 
2141
 
    def get_format_string(self):
 
2163
    @classmethod
 
2164
    def get_format_string(cls):
2142
2165
        """See BranchFormat.get_format_string()."""
2143
2166
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2144
2167
 
2149
2172
    def supports_set_append_revisions_only(self):
2150
2173
        return True
2151
2174
 
 
2175
    def supports_stacking(self):
 
2176
        return True
 
2177
 
 
2178
    def make_tags(self, branch):
 
2179
        """See breezy.branch.BranchFormat.make_tags()."""
 
2180
        return _mod_tag.BasicTags(branch)
 
2181
 
2152
2182
    supports_reference_locations = False
2153
2183
 
2154
2184
 
2155
 
class BranchReferenceFormat(BranchFormat):
 
2185
class BranchReferenceFormat(BranchFormatMetadir):
2156
2186
    """Bzr branch reference format.
2157
2187
 
2158
2188
    Branch references are used in implementing checkouts, they
2163
2193
     - a format string
2164
2194
    """
2165
2195
 
2166
 
    def get_format_string(self):
 
2196
    @classmethod
 
2197
    def get_format_string(cls):
2167
2198
        """See BranchFormat.get_format_string()."""
2168
2199
        return "Bazaar-NG Branch Reference Format 1\n"
2169
2200
 
2181
2212
        transport = a_bzrdir.get_branch_transport(None, name=name)
2182
2213
        location = transport.put_bytes('location', to_branch.base)
2183
2214
 
2184
 
    def initialize(self, a_bzrdir, name=None, target_branch=None):
 
2215
    def initialize(self, a_bzrdir, name=None, target_branch=None,
 
2216
            repository=None, append_revisions_only=None):
2185
2217
        """Create a branch of this format in a_bzrdir."""
2186
2218
        if target_branch is None:
2187
2219
            # this format does not implement branch itself, thus the implicit
2188
2220
            # creation contract must see it as uninitializable
2189
2221
            raise errors.UninitializableFormat(self)
2190
2222
        mutter('creating branch reference in %s', a_bzrdir.user_url)
 
2223
        if a_bzrdir._format.fixed_components:
 
2224
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
 
2225
        if name is None:
 
2226
            name = a_bzrdir._get_selected_branch()
2191
2227
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2192
2228
        branch_transport.put_bytes('location',
2193
 
            target_branch.bzrdir.user_url)
2194
 
        branch_transport.put_bytes('format', self.get_format_string())
2195
 
        branch = self.open(
2196
 
            a_bzrdir, name, _found=True,
 
2229
            target_branch.user_url)
 
2230
        branch_transport.put_bytes('format', self.as_string())
 
2231
        branch = self.open(a_bzrdir, name, _found=True,
2197
2232
            possible_transports=[target_branch.bzrdir.root_transport])
2198
2233
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2199
2234
        return branch
2200
2235
 
2201
 
    def __init__(self):
2202
 
        super(BranchReferenceFormat, self).__init__()
2203
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2204
 
        self._matchingbzrdir.set_branch_format(self)
2205
 
 
2206
2236
    def _make_reference_clone_function(format, a_branch):
2207
2237
        """Create a clone() routine for a branch dynamically."""
2208
2238
        def clone(to_bzrdir, revision_id=None,
2215
2245
        return clone
2216
2246
 
2217
2247
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2218
 
             possible_transports=None, ignore_fallbacks=False):
 
2248
             possible_transports=None, ignore_fallbacks=False,
 
2249
             found_repository=None):
2219
2250
        """Return the branch that the branch reference in a_bzrdir points at.
2220
2251
 
2221
2252
        :param a_bzrdir: A BzrDir that contains a branch.
2229
2260
            a_bzrdir.
2230
2261
        :param possible_transports: An optional reusable transports list.
2231
2262
        """
 
2263
        if name is None:
 
2264
            name = a_bzrdir._get_selected_branch()
2232
2265
        if not _found:
2233
 
            format = BranchFormat.find_format(a_bzrdir, name=name)
 
2266
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2234
2267
            if format.__class__ != self.__class__:
2235
2268
                raise AssertionError("wrong format %r found for %r" %
2236
2269
                    (format, self))
2237
2270
        if location is None:
2238
2271
            location = self.get_reference(a_bzrdir, name)
2239
 
        real_bzrdir = bzrdir.BzrDir.open(
 
2272
        real_bzrdir = controldir.ControlDir.open(
2240
2273
            location, possible_transports=possible_transports)
2241
 
        result = real_bzrdir.open_branch(name=name, 
2242
 
            ignore_fallbacks=ignore_fallbacks)
 
2274
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
 
2275
            possible_transports=possible_transports)
2243
2276
        # this changes the behaviour of result.clone to create a new reference
2244
2277
        # rather than a copy of the content of the branch.
2245
2278
        # I did not use a proxy object because that needs much more extensive
2252
2285
        return result
2253
2286
 
2254
2287
 
 
2288
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
 
2289
    """Branch format registry."""
 
2290
 
 
2291
    def __init__(self, other_registry=None):
 
2292
        super(BranchFormatRegistry, self).__init__(other_registry)
 
2293
        self._default_format = None
 
2294
 
 
2295
    def set_default(self, format):
 
2296
        self._default_format = format
 
2297
 
 
2298
    def get_default(self):
 
2299
        return self._default_format
 
2300
 
 
2301
 
2255
2302
network_format_registry = registry.FormatRegistry()
2256
2303
"""Registry of formats indexed by their network name.
2257
2304
 
2260
2307
BranchFormat.network_name() for more detail.
2261
2308
"""
2262
2309
 
 
2310
format_registry = BranchFormatRegistry(network_format_registry)
 
2311
 
2263
2312
 
2264
2313
# formats which have no format string are not discoverable
2265
2314
# and not independently creatable, so are not registered.
2266
 
__format5 = BzrBranchFormat5()
2267
2315
__format6 = BzrBranchFormat6()
2268
2316
__format7 = BzrBranchFormat7()
2269
2317
__format8 = BzrBranchFormat8()
2270
 
BranchFormat.register_format(__format5)
2271
 
BranchFormat.register_format(BranchReferenceFormat())
2272
 
BranchFormat.register_format(__format6)
2273
 
BranchFormat.register_format(__format7)
2274
 
BranchFormat.register_format(__format8)
2275
 
BranchFormat.set_default_format(__format7)
2276
 
_legacy_formats = [BzrBranchFormat4(),
2277
 
    ]
2278
 
network_format_registry.register(
2279
 
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
 
2318
format_registry.register_lazy(
 
2319
    "Bazaar-NG branch format 5\n", "breezy.branchfmt.fullhistory", "BzrBranchFormat5")
 
2320
format_registry.register(BranchReferenceFormat())
 
2321
format_registry.register(__format6)
 
2322
format_registry.register(__format7)
 
2323
format_registry.register(__format8)
 
2324
format_registry.set_default(__format7)
2280
2325
 
2281
2326
 
2282
2327
class BranchWriteLockResult(LogicalLockResult):
2314
2359
 
2315
2360
    def __init__(self, _format=None,
2316
2361
                 _control_files=None, a_bzrdir=None, name=None,
2317
 
                 _repository=None, ignore_fallbacks=False):
 
2362
                 _repository=None, ignore_fallbacks=False,
 
2363
                 possible_transports=None):
2318
2364
        """Create new branch object at a particular location."""
2319
2365
        if a_bzrdir is None:
2320
2366
            raise ValueError('a_bzrdir must be supplied')
2321
 
        else:
2322
 
            self.bzrdir = a_bzrdir
2323
 
        self._base = self.bzrdir.transport.clone('..').base
 
2367
        if name is None:
 
2368
            raise ValueError('name must be supplied')
 
2369
        self.bzrdir = a_bzrdir
 
2370
        self._user_transport = self.bzrdir.transport.clone('..')
 
2371
        if name != "":
 
2372
            self._user_transport.set_segment_parameter(
 
2373
                "branch", urlutils.escape(name))
 
2374
        self._base = self._user_transport.base
2324
2375
        self.name = name
2325
 
        # XXX: We should be able to just do
2326
 
        #   self.base = self.bzrdir.root_transport.base
2327
 
        # but this does not quite work yet -- mbp 20080522
2328
2376
        self._format = _format
2329
2377
        if _control_files is None:
2330
2378
            raise ValueError('BzrBranch _control_files is None')
2331
2379
        self.control_files = _control_files
2332
2380
        self._transport = _control_files._transport
2333
2381
        self.repository = _repository
2334
 
        Branch.__init__(self)
 
2382
        self.conf_store = None
 
2383
        Branch.__init__(self, possible_transports)
2335
2384
 
2336
2385
    def __str__(self):
2337
 
        if self.name is None:
2338
 
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
2339
 
        else:
2340
 
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2341
 
                self.name)
 
2386
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
2342
2387
 
2343
2388
    __repr__ = __str__
2344
2389
 
2348
2393
 
2349
2394
    base = property(_get_base, doc="The URL for the root of this branch.")
2350
2395
 
 
2396
    @property
 
2397
    def user_transport(self):
 
2398
        return self._user_transport
 
2399
 
2351
2400
    def _get_config(self):
2352
 
        return TransportConfig(self._transport, 'branch.conf')
 
2401
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
 
2402
 
 
2403
    def _get_config_store(self):
 
2404
        if self.conf_store is None:
 
2405
            self.conf_store =  _mod_config.BranchStore(self)
 
2406
        return self.conf_store
 
2407
 
 
2408
    def _uncommitted_branch(self):
 
2409
        """Return the branch that may contain uncommitted changes."""
 
2410
        master = self.get_master_branch()
 
2411
        if master is not None:
 
2412
            return master
 
2413
        else:
 
2414
            return self
 
2415
 
 
2416
    def store_uncommitted(self, creator):
 
2417
        """Store uncommitted changes from a ShelfCreator.
 
2418
 
 
2419
        :param creator: The ShelfCreator containing uncommitted changes, or
 
2420
            None to delete any stored changes.
 
2421
        :raises: ChangesAlreadyStored if the branch already has changes.
 
2422
        """
 
2423
        branch = self._uncommitted_branch()
 
2424
        if creator is None:
 
2425
            branch._transport.delete('stored-transform')
 
2426
            return
 
2427
        if branch._transport.has('stored-transform'):
 
2428
            raise errors.ChangesAlreadyStored
 
2429
        transform = BytesIO()
 
2430
        creator.write_shelf(transform)
 
2431
        transform.seek(0)
 
2432
        branch._transport.put_file('stored-transform', transform)
 
2433
 
 
2434
    def get_unshelver(self, tree):
 
2435
        """Return a shelf.Unshelver for this branch and tree.
 
2436
 
 
2437
        :param tree: The tree to use to construct the Unshelver.
 
2438
        :return: an Unshelver or None if no changes are stored.
 
2439
        """
 
2440
        branch = self._uncommitted_branch()
 
2441
        try:
 
2442
            transform = branch._transport.get('stored-transform')
 
2443
        except errors.NoSuchFile:
 
2444
            return None
 
2445
        return shelf.Unshelver.from_tree_and_shelf(tree, transform)
2353
2446
 
2354
2447
    def is_locked(self):
2355
2448
        return self.control_files.is_locked()
2363
2456
        """
2364
2457
        if not self.is_locked():
2365
2458
            self._note_lock('w')
2366
 
        # All-in-one needs to always unlock/lock.
2367
 
        repo_control = getattr(self.repository, 'control_files', None)
2368
 
        if self.control_files == repo_control or not self.is_locked():
2369
2459
            self.repository._warn_if_deprecated(self)
2370
2460
            self.repository.lock_write()
2371
2461
            took_lock = True
2382
2472
    def lock_read(self):
2383
2473
        """Lock the branch for read operations.
2384
2474
 
2385
 
        :return: A bzrlib.lock.LogicalLockResult.
 
2475
        :return: A breezy.lock.LogicalLockResult.
2386
2476
        """
2387
2477
        if not self.is_locked():
2388
2478
            self._note_lock('r')
2389
 
        # All-in-one needs to always unlock/lock.
2390
 
        repo_control = getattr(self.repository, 'control_files', None)
2391
 
        if self.control_files == repo_control or not self.is_locked():
2392
2479
            self.repository._warn_if_deprecated(self)
2393
2480
            self.repository.lock_read()
2394
2481
            took_lock = True
2404
2491
 
2405
2492
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2406
2493
    def unlock(self):
 
2494
        if self.control_files._lock_count == 1 and self.conf_store is not None:
 
2495
            self.conf_store.save_changes()
2407
2496
        try:
2408
2497
            self.control_files.unlock()
2409
2498
        finally:
2410
 
            # All-in-one needs to always unlock/lock.
2411
 
            repo_control = getattr(self.repository, 'control_files', None)
2412
 
            if (self.control_files == repo_control or
2413
 
                not self.control_files.is_locked()):
2414
 
                self.repository.unlock()
2415
2499
            if not self.control_files.is_locked():
 
2500
                self.repository.unlock()
2416
2501
                # we just released the lock
2417
2502
                self._clear_cached_state()
2418
2503
 
2430
2515
        """See Branch.print_file."""
2431
2516
        return self.repository.print_file(file, revision_id)
2432
2517
 
2433
 
    def _write_revision_history(self, history):
2434
 
        """Factored out of set_revision_history.
2435
 
 
2436
 
        This performs the actual writing to disk.
2437
 
        It is intended to be called by BzrBranch5.set_revision_history."""
2438
 
        self._transport.put_bytes(
2439
 
            'revision-history', '\n'.join(history),
2440
 
            mode=self.bzrdir._get_file_mode())
2441
 
 
2442
 
    @needs_write_lock
2443
 
    def set_revision_history(self, rev_history):
2444
 
        """See Branch.set_revision_history."""
2445
 
        if 'evil' in debug.debug_flags:
2446
 
            mutter_callsite(3, "set_revision_history scales with history.")
2447
 
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2448
 
        for rev_id in rev_history:
2449
 
            check_not_reserved_id(rev_id)
2450
 
        if Branch.hooks['post_change_branch_tip']:
2451
 
            # Don't calculate the last_revision_info() if there are no hooks
2452
 
            # that will use it.
2453
 
            old_revno, old_revid = self.last_revision_info()
2454
 
        if len(rev_history) == 0:
2455
 
            revid = _mod_revision.NULL_REVISION
2456
 
        else:
2457
 
            revid = rev_history[-1]
2458
 
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2459
 
        self._write_revision_history(rev_history)
2460
 
        self._clear_cached_state()
2461
 
        self._cache_revision_history(rev_history)
2462
 
        for hook in Branch.hooks['set_rh']:
2463
 
            hook(self, rev_history)
2464
 
        if Branch.hooks['post_change_branch_tip']:
2465
 
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2466
 
 
2467
 
    def _synchronize_history(self, destination, revision_id):
2468
 
        """Synchronize last revision and revision history between branches.
2469
 
 
2470
 
        This version is most efficient when the destination is also a
2471
 
        BzrBranch5, but works for BzrBranch6 as long as the revision
2472
 
        history is the true lefthand parent history, and all of the revisions
2473
 
        are in the destination's repository.  If not, set_revision_history
2474
 
        will fail.
2475
 
 
2476
 
        :param destination: The branch to copy the history into
2477
 
        :param revision_id: The revision-id to truncate history at.  May
2478
 
          be None to copy complete history.
2479
 
        """
2480
 
        if not isinstance(destination._format, BzrBranchFormat5):
2481
 
            super(BzrBranch, self)._synchronize_history(
2482
 
                destination, revision_id)
2483
 
            return
2484
 
        if revision_id == _mod_revision.NULL_REVISION:
2485
 
            new_history = []
2486
 
        else:
2487
 
            new_history = self.revision_history()
2488
 
        if revision_id is not None and new_history != []:
2489
 
            try:
2490
 
                new_history = new_history[:new_history.index(revision_id) + 1]
2491
 
            except ValueError:
2492
 
                rev = self.repository.get_revision(revision_id)
2493
 
                new_history = rev.get_history(self.repository)[1:]
2494
 
        destination.set_revision_history(new_history)
2495
 
 
2496
2518
    @needs_write_lock
2497
2519
    def set_last_revision_info(self, revno, revision_id):
2498
 
        """Set the last revision of this branch.
2499
 
 
2500
 
        The caller is responsible for checking that the revno is correct
2501
 
        for this revision id.
2502
 
 
2503
 
        It may be possible to set the branch last revision to an id not
2504
 
        present in the repository.  However, branches can also be
2505
 
        configured to check constraints on history, in which case this may not
2506
 
        be permitted.
2507
 
        """
 
2520
        if not revision_id or not isinstance(revision_id, basestring):
 
2521
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2508
2522
        revision_id = _mod_revision.ensure_null(revision_id)
2509
 
        # this old format stores the full history, but this api doesn't
2510
 
        # provide it, so we must generate, and might as well check it's
2511
 
        # correct
2512
 
        history = self._lefthand_history(revision_id)
2513
 
        if len(history) != revno:
2514
 
            raise AssertionError('%d != %d' % (len(history), revno))
2515
 
        self.set_revision_history(history)
2516
 
 
2517
 
    def _gen_revision_history(self):
2518
 
        history = self._transport.get_bytes('revision-history').split('\n')
2519
 
        if history[-1:] == ['']:
2520
 
            # There shouldn't be a trailing newline, but just in case.
2521
 
            history.pop()
2522
 
        return history
2523
 
 
2524
 
    @needs_write_lock
2525
 
    def generate_revision_history(self, revision_id, last_rev=None,
2526
 
        other_branch=None):
2527
 
        """Create a new revision history that will finish with revision_id.
2528
 
 
2529
 
        :param revision_id: the new tip to use.
2530
 
        :param last_rev: The previous last_revision. If not None, then this
2531
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
2532
 
        :param other_branch: The other branch that DivergedBranches should
2533
 
            raise with respect to.
2534
 
        """
2535
 
        self.set_revision_history(self._lefthand_history(revision_id,
2536
 
            last_rev, other_branch))
 
2523
        old_revno, old_revid = self.last_revision_info()
 
2524
        if self.get_append_revisions_only():
 
2525
            self._check_history_violation(revision_id)
 
2526
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
 
2527
        self._write_last_revision_info(revno, revision_id)
 
2528
        self._clear_cached_state()
 
2529
        self._last_revision_info_cache = revno, revision_id
 
2530
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2537
2531
 
2538
2532
    def basis_tree(self):
2539
2533
        """See Branch.basis_tree."""
2548
2542
                pass
2549
2543
        return None
2550
2544
 
2551
 
    def _basic_push(self, target, overwrite, stop_revision):
2552
 
        """Basic implementation of push without bound branches or hooks.
2553
 
 
2554
 
        Must be called with source read locked and target write locked.
2555
 
        """
2556
 
        result = BranchPushResult()
2557
 
        result.source_branch = self
2558
 
        result.target_branch = target
2559
 
        result.old_revno, result.old_revid = target.last_revision_info()
2560
 
        self.update_references(target)
2561
 
        if result.old_revid != self.last_revision():
2562
 
            # We assume that during 'push' this repository is closer than
2563
 
            # the target.
2564
 
            graph = self.repository.get_graph(target.repository)
2565
 
            target.update_revisions(self, stop_revision,
2566
 
                overwrite=overwrite, graph=graph)
2567
 
        if self._push_should_merge_tags():
2568
 
            result.tag_conflicts = self.tags.merge_to(target.tags,
2569
 
                overwrite)
2570
 
        result.new_revno, result.new_revid = target.last_revision_info()
2571
 
        return result
2572
 
 
2573
2545
    def get_stacked_on_url(self):
2574
2546
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2575
2547
 
2586
2558
            self._transport.put_bytes('parent', url + '\n',
2587
2559
                mode=self.bzrdir._get_file_mode())
2588
2560
 
2589
 
 
2590
 
class BzrBranch5(BzrBranch):
2591
 
    """A format 5 branch. This supports new features over plain branches.
2592
 
 
2593
 
    It has support for a master_branch which is the data for bound branches.
2594
 
    """
2595
 
 
2596
 
    def get_bound_location(self):
2597
 
        try:
2598
 
            return self._transport.get_bytes('bound')[:-1]
2599
 
        except errors.NoSuchFile:
2600
 
            return None
2601
 
 
2602
 
    @needs_read_lock
2603
 
    def get_master_branch(self, possible_transports=None):
2604
 
        """Return the branch we are bound to.
2605
 
 
2606
 
        :return: Either a Branch, or None
2607
 
 
2608
 
        This could memoise the branch, but if thats done
2609
 
        it must be revalidated on each new lock.
2610
 
        So for now we just don't memoise it.
2611
 
        # RBC 20060304 review this decision.
2612
 
        """
2613
 
        bound_loc = self.get_bound_location()
2614
 
        if not bound_loc:
2615
 
            return None
2616
 
        try:
2617
 
            return Branch.open(bound_loc,
2618
 
                               possible_transports=possible_transports)
2619
 
        except (errors.NotBranchError, errors.ConnectionError), e:
2620
 
            raise errors.BoundBranchConnectionFailure(
2621
 
                    self, bound_loc, e)
2622
 
 
2623
2561
    @needs_write_lock
2624
 
    def set_bound_location(self, location):
2625
 
        """Set the target where this branch is bound to.
2626
 
 
2627
 
        :param location: URL to the target branch
2628
 
        """
2629
 
        if location:
2630
 
            self._transport.put_bytes('bound', location+'\n',
2631
 
                mode=self.bzrdir._get_file_mode())
2632
 
        else:
2633
 
            try:
2634
 
                self._transport.delete('bound')
2635
 
            except errors.NoSuchFile:
2636
 
                return False
2637
 
            return True
 
2562
    def unbind(self):
 
2563
        """If bound, unbind"""
 
2564
        return self.set_bound_location(None)
2638
2565
 
2639
2566
    @needs_write_lock
2640
2567
    def bind(self, other):
2662
2589
        # history around
2663
2590
        self.set_bound_location(other.base)
2664
2591
 
 
2592
    def get_bound_location(self):
 
2593
        try:
 
2594
            return self._transport.get_bytes('bound')[:-1]
 
2595
        except errors.NoSuchFile:
 
2596
            return None
 
2597
 
 
2598
    @needs_read_lock
 
2599
    def get_master_branch(self, possible_transports=None):
 
2600
        """Return the branch we are bound to.
 
2601
 
 
2602
        :return: Either a Branch, or None
 
2603
        """
 
2604
        if self._master_branch_cache is None:
 
2605
            self._master_branch_cache = self._get_master_branch(
 
2606
                possible_transports)
 
2607
        return self._master_branch_cache
 
2608
 
 
2609
    def _get_master_branch(self, possible_transports):
 
2610
        bound_loc = self.get_bound_location()
 
2611
        if not bound_loc:
 
2612
            return None
 
2613
        try:
 
2614
            return Branch.open(bound_loc,
 
2615
                               possible_transports=possible_transports)
 
2616
        except (errors.NotBranchError, errors.ConnectionError) as e:
 
2617
            raise errors.BoundBranchConnectionFailure(
 
2618
                    self, bound_loc, e)
 
2619
 
2665
2620
    @needs_write_lock
2666
 
    def unbind(self):
2667
 
        """If bound, unbind"""
2668
 
        return self.set_bound_location(None)
 
2621
    def set_bound_location(self, location):
 
2622
        """Set the target where this branch is bound to.
 
2623
 
 
2624
        :param location: URL to the target branch
 
2625
        """
 
2626
        self._master_branch_cache = None
 
2627
        if location:
 
2628
            self._transport.put_bytes('bound', location+'\n',
 
2629
                mode=self.bzrdir._get_file_mode())
 
2630
        else:
 
2631
            try:
 
2632
                self._transport.delete('bound')
 
2633
            except errors.NoSuchFile:
 
2634
                return False
 
2635
            return True
2669
2636
 
2670
2637
    @needs_write_lock
2671
2638
    def update(self, possible_transports=None):
2684
2651
            return old_tip
2685
2652
        return None
2686
2653
 
2687
 
 
2688
 
class BzrBranch8(BzrBranch5):
 
2654
    def _read_last_revision_info(self):
 
2655
        revision_string = self._transport.get_bytes('last-revision')
 
2656
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
 
2657
        revision_id = cache_utf8.get_cached_utf8(revision_id)
 
2658
        revno = int(revno)
 
2659
        return revno, revision_id
 
2660
 
 
2661
    def _write_last_revision_info(self, revno, revision_id):
 
2662
        """Simply write out the revision id, with no checks.
 
2663
 
 
2664
        Use set_last_revision_info to perform this safely.
 
2665
 
 
2666
        Does not update the revision_history cache.
 
2667
        """
 
2668
        revision_id = _mod_revision.ensure_null(revision_id)
 
2669
        out_string = '%d %s\n' % (revno, revision_id)
 
2670
        self._transport.put_bytes('last-revision', out_string,
 
2671
            mode=self.bzrdir._get_file_mode())
 
2672
 
 
2673
    @needs_write_lock
 
2674
    def update_feature_flags(self, updated_flags):
 
2675
        """Update the feature flags for this branch.
 
2676
 
 
2677
        :param updated_flags: Dictionary mapping feature names to necessities
 
2678
            A necessity can be None to indicate the feature should be removed
 
2679
        """
 
2680
        self._format._update_feature_flags(updated_flags)
 
2681
        self.control_transport.put_bytes('format', self._format.as_string())
 
2682
 
 
2683
 
 
2684
class BzrBranch8(BzrBranch):
2689
2685
    """A branch that stores tree-reference locations."""
2690
2686
 
2691
 
    def _open_hook(self):
 
2687
    def _open_hook(self, possible_transports=None):
2692
2688
        if self._ignore_fallbacks:
2693
2689
            return
 
2690
        if possible_transports is None:
 
2691
            possible_transports = [self.bzrdir.root_transport]
2694
2692
        try:
2695
2693
            url = self.get_stacked_on_url()
2696
2694
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2704
2702
                    raise AssertionError(
2705
2703
                        "'transform_fallback_location' hook %s returned "
2706
2704
                        "None, not a URL." % hook_name)
2707
 
            self._activate_fallback_location(url)
 
2705
            self._activate_fallback_location(url,
 
2706
                possible_transports=possible_transports)
2708
2707
 
2709
2708
    def __init__(self, *args, **kwargs):
2710
2709
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2717
2716
        self._last_revision_info_cache = None
2718
2717
        self._reference_info = None
2719
2718
 
2720
 
    def _last_revision_info(self):
2721
 
        revision_string = self._transport.get_bytes('last-revision')
2722
 
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2723
 
        revision_id = cache_utf8.get_cached_utf8(revision_id)
2724
 
        revno = int(revno)
2725
 
        return revno, revision_id
2726
 
 
2727
 
    def _write_last_revision_info(self, revno, revision_id):
2728
 
        """Simply write out the revision id, with no checks.
2729
 
 
2730
 
        Use set_last_revision_info to perform this safely.
2731
 
 
2732
 
        Does not update the revision_history cache.
2733
 
        Intended to be called by set_last_revision_info and
2734
 
        _write_revision_history.
2735
 
        """
2736
 
        revision_id = _mod_revision.ensure_null(revision_id)
2737
 
        out_string = '%d %s\n' % (revno, revision_id)
2738
 
        self._transport.put_bytes('last-revision', out_string,
2739
 
            mode=self.bzrdir._get_file_mode())
2740
 
 
2741
 
    @needs_write_lock
2742
 
    def set_last_revision_info(self, revno, revision_id):
2743
 
        revision_id = _mod_revision.ensure_null(revision_id)
2744
 
        old_revno, old_revid = self.last_revision_info()
2745
 
        if self._get_append_revisions_only():
2746
 
            self._check_history_violation(revision_id)
2747
 
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2748
 
        self._write_last_revision_info(revno, revision_id)
2749
 
        self._clear_cached_state()
2750
 
        self._last_revision_info_cache = revno, revision_id
2751
 
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2752
 
 
2753
 
    def _synchronize_history(self, destination, revision_id):
2754
 
        """Synchronize last revision and revision history between branches.
2755
 
 
2756
 
        :see: Branch._synchronize_history
2757
 
        """
2758
 
        # XXX: The base Branch has a fast implementation of this method based
2759
 
        # on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2760
 
        # that uses set_revision_history.  This class inherits from BzrBranch5,
2761
 
        # but wants the fast implementation, so it calls
2762
 
        # Branch._synchronize_history directly.
2763
 
        Branch._synchronize_history(self, destination, revision_id)
2764
 
 
2765
2719
    def _check_history_violation(self, revision_id):
2766
 
        last_revision = _mod_revision.ensure_null(self.last_revision())
 
2720
        current_revid = self.last_revision()
 
2721
        last_revision = _mod_revision.ensure_null(current_revid)
2767
2722
        if _mod_revision.is_null(last_revision):
2768
2723
            return
2769
 
        if last_revision not in self._lefthand_history(revision_id):
2770
 
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
 
2724
        graph = self.repository.get_graph()
 
2725
        for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
 
2726
            if lh_ancestor == current_revid:
 
2727
                return
 
2728
        raise errors.AppendRevisionsOnlyViolation(self.user_url)
2771
2729
 
2772
2730
    def _gen_revision_history(self):
2773
2731
        """Generate the revision history from last revision
2776
2734
        self._extend_partial_history(stop_index=last_revno-1)
2777
2735
        return list(reversed(self._partial_revision_history_cache))
2778
2736
 
2779
 
    def _write_revision_history(self, history):
2780
 
        """Factored out of set_revision_history.
2781
 
 
2782
 
        This performs the actual writing to disk, with format-specific checks.
2783
 
        It is intended to be called by BzrBranch5.set_revision_history.
2784
 
        """
2785
 
        if len(history) == 0:
2786
 
            last_revision = 'null:'
2787
 
        else:
2788
 
            if history != self._lefthand_history(history[-1]):
2789
 
                raise errors.NotLefthandHistory(history)
2790
 
            last_revision = history[-1]
2791
 
        if self._get_append_revisions_only():
2792
 
            self._check_history_violation(last_revision)
2793
 
        self._write_last_revision_info(len(history), last_revision)
2794
 
 
2795
2737
    @needs_write_lock
2796
2738
    def _set_parent_location(self, url):
2797
2739
        """Set the parent branch"""
2808
2750
 
2809
2751
        :param info_dict: A dict of {file_id: (tree_path, branch_location)}
2810
2752
        """
2811
 
        s = StringIO()
 
2753
        s = BytesIO()
2812
2754
        writer = rio.RioWriter(s)
2813
2755
        for key, (tree_path, branch_location) in info_dict.iteritems():
2814
2756
            stanza = rio.Stanza(file_id=key, tree_path=tree_path,
2883
2825
 
2884
2826
    def set_bound_location(self, location):
2885
2827
        """See Branch.set_push_location."""
 
2828
        self._master_branch_cache = None
2886
2829
        result = None
2887
 
        config = self.get_config()
 
2830
        conf = self.get_config_stack()
2888
2831
        if location is None:
2889
 
            if config.get_user_option('bound') != 'True':
 
2832
            if not conf.get('bound'):
2890
2833
                return False
2891
2834
            else:
2892
 
                config.set_user_option('bound', 'False', warn_masked=True)
 
2835
                conf.set('bound', 'False')
2893
2836
                return True
2894
2837
        else:
2895
2838
            self._set_config_location('bound_location', location,
2896
 
                                      config=config)
2897
 
            config.set_user_option('bound', 'True', warn_masked=True)
 
2839
                                      config=conf)
 
2840
            conf.set('bound', 'True')
2898
2841
        return True
2899
2842
 
2900
2843
    def _get_bound_location(self, bound):
2901
2844
        """Return the bound location in the config file.
2902
2845
 
2903
2846
        Return None if the bound parameter does not match"""
2904
 
        config = self.get_config()
2905
 
        config_bound = (config.get_user_option('bound') == 'True')
2906
 
        if config_bound != bound:
 
2847
        conf = self.get_config_stack()
 
2848
        if conf.get('bound') != bound:
2907
2849
            return None
2908
 
        return self._get_config_location('bound_location', config=config)
 
2850
        return self._get_config_location('bound_location', config=conf)
2909
2851
 
2910
2852
    def get_bound_location(self):
2911
 
        """See Branch.set_push_location."""
 
2853
        """See Branch.get_bound_location."""
2912
2854
        return self._get_bound_location(True)
2913
2855
 
2914
2856
    def get_old_bound_location(self):
2919
2861
        # you can always ask for the URL; but you might not be able to use it
2920
2862
        # if the repo can't support stacking.
2921
2863
        ## self._check_stackable_repo()
2922
 
        stacked_url = self._get_config_location('stacked_on_location')
 
2864
        # stacked_on_location is only ever defined in branch.conf, so don't
 
2865
        # waste effort reading the whole stack of config files.
 
2866
        conf = _mod_config.BranchOnlyStack(self)
 
2867
        stacked_url = self._get_config_location('stacked_on_location',
 
2868
                                                config=conf)
2923
2869
        if stacked_url is None:
2924
2870
            raise errors.NotStacked(self)
2925
 
        return stacked_url
2926
 
 
2927
 
    def _get_append_revisions_only(self):
2928
 
        return self.get_config(
2929
 
            ).get_user_option_as_bool('append_revisions_only')
2930
 
 
2931
 
    @needs_write_lock
2932
 
    def generate_revision_history(self, revision_id, last_rev=None,
2933
 
                                  other_branch=None):
2934
 
        """See BzrBranch5.generate_revision_history"""
2935
 
        history = self._lefthand_history(revision_id, last_rev, other_branch)
2936
 
        revno = len(history)
2937
 
        self.set_last_revision_info(revno, revision_id)
 
2871
        return stacked_url.encode('utf-8')
2938
2872
 
2939
2873
    @needs_read_lock
2940
2874
    def get_rev_id(self, revno, history=None):
2965
2899
        try:
2966
2900
            index = self._partial_revision_history_cache.index(revision_id)
2967
2901
        except ValueError:
2968
 
            self._extend_partial_history(stop_revision=revision_id)
 
2902
            try:
 
2903
                self._extend_partial_history(stop_revision=revision_id)
 
2904
            except errors.RevisionNotPresent as e:
 
2905
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
2969
2906
            index = len(self._partial_revision_history_cache) - 1
 
2907
            if index < 0:
 
2908
                raise errors.NoSuchRevision(self, revision_id)
2970
2909
            if self._partial_revision_history_cache[index] != revision_id:
2971
2910
                raise errors.NoSuchRevision(self, revision_id)
2972
2911
        return self.revno() - index
3024
2963
    :ivar local_branch: target branch if there is a Master, else None
3025
2964
    :ivar target_branch: Target/destination branch object. (write locked)
3026
2965
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
 
2966
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3027
2967
    """
3028
2968
 
3029
 
    def __int__(self):
3030
 
        # DEPRECATED: pull used to return the change in revno
3031
 
        return self.new_revno - self.old_revno
3032
 
 
3033
2969
    def report(self, to_file):
 
2970
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
2971
        tag_updates = getattr(self, "tag_updates", None)
3034
2972
        if not is_quiet():
3035
 
            if self.old_revid == self.new_revid:
3036
 
                to_file.write('No revisions to pull.\n')
3037
 
            else:
 
2973
            if self.old_revid != self.new_revid:
3038
2974
                to_file.write('Now on revision %d.\n' % self.new_revno)
 
2975
            if tag_updates:
 
2976
                to_file.write('%d tag(s) updated.\n' % len(tag_updates))
 
2977
            if self.old_revid == self.new_revid and not tag_updates:
 
2978
                if not tag_conflicts:
 
2979
                    to_file.write('No revisions or tags to pull.\n')
 
2980
                else:
 
2981
                    to_file.write('No revisions to pull.\n')
3039
2982
        self._show_tag_conficts(to_file)
3040
2983
 
3041
2984
 
3058
3001
        target, otherwise it will be None.
3059
3002
    """
3060
3003
 
3061
 
    def __int__(self):
3062
 
        # DEPRECATED: push used to return the change in revno
3063
 
        return self.new_revno - self.old_revno
3064
 
 
3065
3004
    def report(self, to_file):
3066
 
        """Write a human-readable description of the result."""
3067
 
        if self.old_revid == self.new_revid:
3068
 
            note('No new revisions to push.')
3069
 
        else:
3070
 
            note('Pushed up to revision %d.' % self.new_revno)
 
3005
        # TODO: This function gets passed a to_file, but then
 
3006
        # ignores it and calls note() instead. This is also
 
3007
        # inconsistent with PullResult(), which writes to stdout.
 
3008
        # -- JRV20110901, bug #838853
 
3009
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
3010
        tag_updates = getattr(self, "tag_updates", None)
 
3011
        if not is_quiet():
 
3012
            if self.old_revid != self.new_revid:
 
3013
                note(gettext('Pushed up to revision %d.') % self.new_revno)
 
3014
            if tag_updates:
 
3015
                note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
 
3016
            if self.old_revid == self.new_revid and not tag_updates:
 
3017
                if not tag_conflicts:
 
3018
                    note(gettext('No new revisions or tags to push.'))
 
3019
                else:
 
3020
                    note(gettext('No new revisions to push.'))
3071
3021
        self._show_tag_conficts(to_file)
3072
3022
 
3073
3023
 
3087
3037
        :param verbose: Requests more detailed display of what was checked,
3088
3038
            if any.
3089
3039
        """
3090
 
        note('checked branch %s format %s', self.branch.user_url,
3091
 
            self.branch._format)
 
3040
        note(gettext('checked branch {0} format {1}').format(
 
3041
                                self.branch.user_url, self.branch._format))
3092
3042
        for error in self.errors:
3093
 
            note('found error:%s', error)
 
3043
            note(gettext('found error:%s'), error)
3094
3044
 
3095
3045
 
3096
3046
class Converter5to6(object):
3103
3053
 
3104
3054
        # Copy source data into target
3105
3055
        new_branch._write_last_revision_info(*branch.last_revision_info())
3106
 
        new_branch.set_parent(branch.get_parent())
3107
 
        new_branch.set_bound_location(branch.get_bound_location())
3108
 
        new_branch.set_push_location(branch.get_push_location())
 
3056
        new_branch.lock_write()
 
3057
        try:
 
3058
            new_branch.set_parent(branch.get_parent())
 
3059
            new_branch.set_bound_location(branch.get_bound_location())
 
3060
            new_branch.set_push_location(branch.get_push_location())
 
3061
        finally:
 
3062
            new_branch.unlock()
3109
3063
 
3110
3064
        # New branch has no tags by default
3111
3065
        new_branch.tags._set_tag_dict({})
3112
3066
 
3113
3067
        # Copying done; now update target format
3114
3068
        new_branch._transport.put_bytes('format',
3115
 
            format.get_format_string(),
 
3069
            format.as_string(),
3116
3070
            mode=new_branch.bzrdir._get_file_mode())
3117
3071
 
3118
3072
        # Clean up old files
3119
3073
        new_branch._transport.delete('revision-history')
 
3074
        branch.lock_write()
3120
3075
        try:
3121
 
            branch.set_parent(None)
3122
 
        except errors.NoSuchFile:
3123
 
            pass
3124
 
        branch.set_bound_location(None)
 
3076
            try:
 
3077
                branch.set_parent(None)
 
3078
            except errors.NoSuchFile:
 
3079
                pass
 
3080
            branch.set_bound_location(None)
 
3081
        finally:
 
3082
            branch.unlock()
3125
3083
 
3126
3084
 
3127
3085
class Converter6to7(object):
3131
3089
        format = BzrBranchFormat7()
3132
3090
        branch._set_config_location('stacked_on_location', '')
3133
3091
        # update target format
3134
 
        branch._transport.put_bytes('format', format.get_format_string())
 
3092
        branch._transport.put_bytes('format', format.as_string())
3135
3093
 
3136
3094
 
3137
3095
class Converter7to8(object):
3138
 
    """Perform an in-place upgrade of format 6 to format 7"""
 
3096
    """Perform an in-place upgrade of format 7 to format 8"""
3139
3097
 
3140
3098
    def convert(self, branch):
3141
3099
        format = BzrBranchFormat8()
3142
3100
        branch._transport.put_bytes('references', '')
3143
3101
        # update target format
3144
 
        branch._transport.put_bytes('format', format.get_format_string())
3145
 
 
3146
 
 
3147
 
def _run_with_write_locked_target(target, callable, *args, **kwargs):
3148
 
    """Run ``callable(*args, **kwargs)``, write-locking target for the
3149
 
    duration.
3150
 
 
3151
 
    _run_with_write_locked_target will attempt to release the lock it acquires.
3152
 
 
3153
 
    If an exception is raised by callable, then that exception *will* be
3154
 
    propagated, even if the unlock attempt raises its own error.  Thus
3155
 
    _run_with_write_locked_target should be preferred to simply doing::
3156
 
 
3157
 
        target.lock_write()
3158
 
        try:
3159
 
            return callable(*args, **kwargs)
3160
 
        finally:
3161
 
            target.unlock()
3162
 
 
3163
 
    """
3164
 
    # This is very similar to bzrlib.decorators.needs_write_lock.  Perhaps they
3165
 
    # should share code?
3166
 
    target.lock_write()
3167
 
    try:
3168
 
        result = callable(*args, **kwargs)
3169
 
    except:
3170
 
        exc_info = sys.exc_info()
3171
 
        try:
3172
 
            target.unlock()
3173
 
        finally:
3174
 
            raise exc_info[0], exc_info[1], exc_info[2]
3175
 
    else:
3176
 
        target.unlock()
3177
 
        return result
 
3102
        branch._transport.put_bytes('format', format.as_string())
3178
3103
 
3179
3104
 
3180
3105
class InterBranch(InterObject):
3188
3113
    _optimisers = []
3189
3114
    """The available optimised InterBranch types."""
3190
3115
 
3191
 
    @staticmethod
3192
 
    def _get_branch_formats_to_test():
3193
 
        """Return a tuple with the Branch formats to use when testing."""
3194
 
        raise NotImplementedError(InterBranch._get_branch_formats_to_test)
 
3116
    @classmethod
 
3117
    def _get_branch_formats_to_test(klass):
 
3118
        """Return an iterable of format tuples for testing.
 
3119
        
 
3120
        :return: An iterable of (from_format, to_format) to use when testing
 
3121
            this InterBranch class. Each InterBranch class should define this
 
3122
            method itself.
 
3123
        """
 
3124
        raise NotImplementedError(klass._get_branch_formats_to_test)
3195
3125
 
 
3126
    @needs_write_lock
3196
3127
    def pull(self, overwrite=False, stop_revision=None,
3197
3128
             possible_transports=None, local=False):
3198
3129
        """Mirror source into target branch.
3203
3134
        """
3204
3135
        raise NotImplementedError(self.pull)
3205
3136
 
3206
 
    def update_revisions(self, stop_revision=None, overwrite=False,
3207
 
                         graph=None):
3208
 
        """Pull in new perfect-fit revisions.
3209
 
 
3210
 
        :param stop_revision: Updated until the given revision
3211
 
        :param overwrite: Always set the branch pointer, rather than checking
3212
 
            to see if it is a proper descendant.
3213
 
        :param graph: A Graph object that can be used to query history
3214
 
            information. This can be None.
3215
 
        :return: None
3216
 
        """
3217
 
        raise NotImplementedError(self.update_revisions)
3218
 
 
3219
 
    def push(self, overwrite=False, stop_revision=None,
 
3137
    @needs_write_lock
 
3138
    def push(self, overwrite=False, stop_revision=None, lossy=False,
3220
3139
             _override_hook_source_branch=None):
3221
3140
        """Mirror the source branch into the target branch.
3222
3141
 
3224
3143
        """
3225
3144
        raise NotImplementedError(self.push)
3226
3145
 
 
3146
    @needs_write_lock
 
3147
    def copy_content_into(self, revision_id=None):
 
3148
        """Copy the content of source into target
 
3149
 
 
3150
        revision_id: if not None, the revision history in the new branch will
 
3151
                     be truncated to end with revision_id.
 
3152
        """
 
3153
        raise NotImplementedError(self.copy_content_into)
 
3154
 
 
3155
    @needs_write_lock
 
3156
    def fetch(self, stop_revision=None, limit=None):
 
3157
        """Fetch revisions.
 
3158
 
 
3159
        :param stop_revision: Last revision to fetch
 
3160
        :param limit: Optional rough limit of revisions to fetch
 
3161
        """
 
3162
        raise NotImplementedError(self.fetch)
 
3163
 
 
3164
 
 
3165
def _fix_overwrite_type(overwrite):
 
3166
    if isinstance(overwrite, bool):
 
3167
        if overwrite:
 
3168
            return ["history", "tags"]
 
3169
        else:
 
3170
            return []
 
3171
    return overwrite
 
3172
 
3227
3173
 
3228
3174
class GenericInterBranch(InterBranch):
3229
 
    """InterBranch implementation that uses public Branch functions.
3230
 
    """
3231
 
 
3232
 
    @staticmethod
3233
 
    def _get_branch_formats_to_test():
3234
 
        return BranchFormat._default_format, BranchFormat._default_format
3235
 
 
3236
 
    def update_revisions(self, stop_revision=None, overwrite=False,
3237
 
        graph=None):
3238
 
        """See InterBranch.update_revisions()."""
 
3175
    """InterBranch implementation that uses public Branch functions."""
 
3176
 
 
3177
    @classmethod
 
3178
    def is_compatible(klass, source, target):
 
3179
        # GenericBranch uses the public API, so always compatible
 
3180
        return True
 
3181
 
 
3182
    @classmethod
 
3183
    def _get_branch_formats_to_test(klass):
 
3184
        return [(format_registry.get_default(), format_registry.get_default())]
 
3185
 
 
3186
    @classmethod
 
3187
    def unwrap_format(klass, format):
 
3188
        if isinstance(format, remote.RemoteBranchFormat):
 
3189
            format._ensure_real()
 
3190
            return format._custom_format
 
3191
        return format
 
3192
 
 
3193
    @needs_write_lock
 
3194
    def copy_content_into(self, revision_id=None):
 
3195
        """Copy the content of source into target
 
3196
 
 
3197
        revision_id: if not None, the revision history in the new branch will
 
3198
                     be truncated to end with revision_id.
 
3199
        """
 
3200
        self.source.update_references(self.target)
 
3201
        self.source._synchronize_history(self.target, revision_id)
 
3202
        try:
 
3203
            parent = self.source.get_parent()
 
3204
        except errors.InaccessibleParent as e:
 
3205
            mutter('parent was not accessible to copy: %s', e)
 
3206
        else:
 
3207
            if parent:
 
3208
                self.target.set_parent(parent)
 
3209
        if self.source._push_should_merge_tags():
 
3210
            self.source.tags.merge_to(self.target.tags)
 
3211
 
 
3212
    @needs_write_lock
 
3213
    def fetch(self, stop_revision=None, limit=None):
 
3214
        if self.target.base == self.source.base:
 
3215
            return (0, [])
3239
3216
        self.source.lock_read()
3240
3217
        try:
3241
 
            other_revno, other_last_revision = self.source.last_revision_info()
3242
 
            stop_revno = None # unknown
3243
 
            if stop_revision is None:
3244
 
                stop_revision = other_last_revision
3245
 
                if _mod_revision.is_null(stop_revision):
3246
 
                    # if there are no commits, we're done.
3247
 
                    return
3248
 
                stop_revno = other_revno
3249
 
 
3250
 
            # what's the current last revision, before we fetch [and change it
3251
 
            # possibly]
3252
 
            last_rev = _mod_revision.ensure_null(self.target.last_revision())
3253
 
            # we fetch here so that we don't process data twice in the common
3254
 
            # case of having something to pull, and so that the check for
3255
 
            # already merged can operate on the just fetched graph, which will
3256
 
            # be cached in memory.
3257
 
            self.target.fetch(self.source, stop_revision)
3258
 
            # Check to see if one is an ancestor of the other
3259
 
            if not overwrite:
3260
 
                if graph is None:
3261
 
                    graph = self.target.repository.get_graph()
3262
 
                if self.target._check_if_descendant_or_diverged(
3263
 
                        stop_revision, last_rev, graph, self.source):
3264
 
                    # stop_revision is a descendant of last_rev, but we aren't
3265
 
                    # overwriting, so we're done.
3266
 
                    return
3267
 
            if stop_revno is None:
3268
 
                if graph is None:
3269
 
                    graph = self.target.repository.get_graph()
3270
 
                this_revno, this_last_revision = \
3271
 
                        self.target.last_revision_info()
3272
 
                stop_revno = graph.find_distance_to_null(stop_revision,
3273
 
                                [(other_last_revision, other_revno),
3274
 
                                 (this_last_revision, this_revno)])
3275
 
            self.target.set_last_revision_info(stop_revno, stop_revision)
 
3218
            fetch_spec_factory = fetch.FetchSpecFactory()
 
3219
            fetch_spec_factory.source_branch = self.source
 
3220
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
 
3221
            fetch_spec_factory.source_repo = self.source.repository
 
3222
            fetch_spec_factory.target_repo = self.target.repository
 
3223
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
 
3224
            fetch_spec_factory.limit = limit
 
3225
            fetch_spec = fetch_spec_factory.make_fetch_spec()
 
3226
            return self.target.repository.fetch(self.source.repository,
 
3227
                fetch_spec=fetch_spec)
3276
3228
        finally:
3277
3229
            self.source.unlock()
3278
3230
 
 
3231
    @needs_write_lock
 
3232
    def _update_revisions(self, stop_revision=None, overwrite=False,
 
3233
            graph=None):
 
3234
        other_revno, other_last_revision = self.source.last_revision_info()
 
3235
        stop_revno = None # unknown
 
3236
        if stop_revision is None:
 
3237
            stop_revision = other_last_revision
 
3238
            if _mod_revision.is_null(stop_revision):
 
3239
                # if there are no commits, we're done.
 
3240
                return
 
3241
            stop_revno = other_revno
 
3242
 
 
3243
        # what's the current last revision, before we fetch [and change it
 
3244
        # possibly]
 
3245
        last_rev = _mod_revision.ensure_null(self.target.last_revision())
 
3246
        # we fetch here so that we don't process data twice in the common
 
3247
        # case of having something to pull, and so that the check for
 
3248
        # already merged can operate on the just fetched graph, which will
 
3249
        # be cached in memory.
 
3250
        self.fetch(stop_revision=stop_revision)
 
3251
        # Check to see if one is an ancestor of the other
 
3252
        if not overwrite:
 
3253
            if graph is None:
 
3254
                graph = self.target.repository.get_graph()
 
3255
            if self.target._check_if_descendant_or_diverged(
 
3256
                    stop_revision, last_rev, graph, self.source):
 
3257
                # stop_revision is a descendant of last_rev, but we aren't
 
3258
                # overwriting, so we're done.
 
3259
                return
 
3260
        if stop_revno is None:
 
3261
            if graph is None:
 
3262
                graph = self.target.repository.get_graph()
 
3263
            this_revno, this_last_revision = \
 
3264
                    self.target.last_revision_info()
 
3265
            stop_revno = graph.find_distance_to_null(stop_revision,
 
3266
                            [(other_last_revision, other_revno),
 
3267
                             (this_last_revision, this_revno)])
 
3268
        self.target.set_last_revision_info(stop_revno, stop_revision)
 
3269
 
 
3270
    @needs_write_lock
3279
3271
    def pull(self, overwrite=False, stop_revision=None,
 
3272
             possible_transports=None, run_hooks=True,
 
3273
             _override_hook_target=None, local=False):
 
3274
        """Pull from source into self, updating my master if any.
 
3275
 
 
3276
        :param run_hooks: Private parameter - if false, this branch
 
3277
            is being called because it's the master of the primary branch,
 
3278
            so it should not run its hooks.
 
3279
        """
 
3280
        bound_location = self.target.get_bound_location()
 
3281
        if local and not bound_location:
 
3282
            raise errors.LocalRequiresBoundBranch()
 
3283
        master_branch = None
 
3284
        source_is_master = False
 
3285
        if bound_location:
 
3286
            # bound_location comes from a config file, some care has to be
 
3287
            # taken to relate it to source.user_url
 
3288
            normalized = urlutils.normalize_url(bound_location)
 
3289
            try:
 
3290
                relpath = self.source.user_transport.relpath(normalized)
 
3291
                source_is_master = (relpath == '')
 
3292
            except (errors.PathNotChild, errors.InvalidURL):
 
3293
                source_is_master = False
 
3294
        if not local and bound_location and not source_is_master:
 
3295
            # not pulling from master, so we need to update master.
 
3296
            master_branch = self.target.get_master_branch(possible_transports)
 
3297
            master_branch.lock_write()
 
3298
        try:
 
3299
            if master_branch:
 
3300
                # pull from source into master.
 
3301
                master_branch.pull(self.source, overwrite, stop_revision,
 
3302
                    run_hooks=False)
 
3303
            return self._pull(overwrite,
 
3304
                stop_revision, _hook_master=master_branch,
 
3305
                run_hooks=run_hooks,
 
3306
                _override_hook_target=_override_hook_target,
 
3307
                merge_tags_to_master=not source_is_master)
 
3308
        finally:
 
3309
            if master_branch:
 
3310
                master_branch.unlock()
 
3311
 
 
3312
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
3313
             _override_hook_source_branch=None):
 
3314
        """See InterBranch.push.
 
3315
 
 
3316
        This is the basic concrete implementation of push()
 
3317
 
 
3318
        :param _override_hook_source_branch: If specified, run the hooks
 
3319
            passing this Branch as the source, rather than self.  This is for
 
3320
            use of RemoteBranch, where push is delegated to the underlying
 
3321
            vfs-based Branch.
 
3322
        """
 
3323
        if lossy:
 
3324
            raise errors.LossyPushToSameVCS(self.source, self.target)
 
3325
        # TODO: Public option to disable running hooks - should be trivial but
 
3326
        # needs tests.
 
3327
 
 
3328
        op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
 
3329
        op.add_cleanup(self.source.lock_read().unlock)
 
3330
        op.add_cleanup(self.target.lock_write().unlock)
 
3331
        return op.run(overwrite, stop_revision,
 
3332
            _override_hook_source_branch=_override_hook_source_branch)
 
3333
 
 
3334
    def _basic_push(self, overwrite, stop_revision):
 
3335
        """Basic implementation of push without bound branches or hooks.
 
3336
 
 
3337
        Must be called with source read locked and target write locked.
 
3338
        """
 
3339
        result = BranchPushResult()
 
3340
        result.source_branch = self.source
 
3341
        result.target_branch = self.target
 
3342
        result.old_revno, result.old_revid = self.target.last_revision_info()
 
3343
        self.source.update_references(self.target)
 
3344
        overwrite = _fix_overwrite_type(overwrite)
 
3345
        if result.old_revid != stop_revision:
 
3346
            # We assume that during 'push' this repository is closer than
 
3347
            # the target.
 
3348
            graph = self.source.repository.get_graph(self.target.repository)
 
3349
            self._update_revisions(stop_revision,
 
3350
                overwrite=("history" in overwrite),
 
3351
                graph=graph)
 
3352
        if self.source._push_should_merge_tags():
 
3353
            result.tag_updates, result.tag_conflicts = (
 
3354
                self.source.tags.merge_to(
 
3355
                self.target.tags, "tags" in overwrite))
 
3356
        result.new_revno, result.new_revid = self.target.last_revision_info()
 
3357
        return result
 
3358
 
 
3359
    def _push_with_bound_branches(self, operation, overwrite, stop_revision,
 
3360
            _override_hook_source_branch=None):
 
3361
        """Push from source into target, and into target's master if any.
 
3362
        """
 
3363
        def _run_hooks():
 
3364
            if _override_hook_source_branch:
 
3365
                result.source_branch = _override_hook_source_branch
 
3366
            for hook in Branch.hooks['post_push']:
 
3367
                hook(result)
 
3368
 
 
3369
        bound_location = self.target.get_bound_location()
 
3370
        if bound_location and self.target.base != bound_location:
 
3371
            # there is a master branch.
 
3372
            #
 
3373
            # XXX: Why the second check?  Is it even supported for a branch to
 
3374
            # be bound to itself? -- mbp 20070507
 
3375
            master_branch = self.target.get_master_branch()
 
3376
            master_branch.lock_write()
 
3377
            operation.add_cleanup(master_branch.unlock)
 
3378
            # push into the master from the source branch.
 
3379
            master_inter = InterBranch.get(self.source, master_branch)
 
3380
            master_inter._basic_push(overwrite, stop_revision)
 
3381
            # and push into the target branch from the source. Note that
 
3382
            # we push from the source branch again, because it's considered
 
3383
            # the highest bandwidth repository.
 
3384
            result = self._basic_push(overwrite, stop_revision)
 
3385
            result.master_branch = master_branch
 
3386
            result.local_branch = self.target
 
3387
        else:
 
3388
            master_branch = None
 
3389
            # no master branch
 
3390
            result = self._basic_push(overwrite, stop_revision)
 
3391
            # TODO: Why set master_branch and local_branch if there's no
 
3392
            # binding?  Maybe cleaner to just leave them unset? -- mbp
 
3393
            # 20070504
 
3394
            result.master_branch = self.target
 
3395
            result.local_branch = None
 
3396
        _run_hooks()
 
3397
        return result
 
3398
 
 
3399
    def _pull(self, overwrite=False, stop_revision=None,
3280
3400
             possible_transports=None, _hook_master=None, run_hooks=True,
3281
 
             _override_hook_target=None, local=False):
 
3401
             _override_hook_target=None, local=False,
 
3402
             merge_tags_to_master=True):
3282
3403
        """See Branch.pull.
3283
3404
 
 
3405
        This function is the core worker, used by GenericInterBranch.pull to
 
3406
        avoid duplication when pulling source->master and source->local.
 
3407
 
3284
3408
        :param _hook_master: Private parameter - set the branch to
3285
3409
            be supplied as the master to pull hooks.
3286
3410
        :param run_hooks: Private parameter - if false, this branch
3287
3411
            is being called because it's the master of the primary branch,
3288
3412
            so it should not run its hooks.
 
3413
            is being called because it's the master of the primary branch,
 
3414
            so it should not run its hooks.
3289
3415
        :param _override_hook_target: Private parameter - set the branch to be
3290
3416
            supplied as the target_branch to pull hooks.
3291
3417
        :param local: Only update the local branch, and not the bound branch.
3310
3436
            # -- JRV20090506
3311
3437
            result.old_revno, result.old_revid = \
3312
3438
                self.target.last_revision_info()
3313
 
            self.target.update_revisions(self.source, stop_revision,
3314
 
                overwrite=overwrite, graph=graph)
 
3439
            overwrite = _fix_overwrite_type(overwrite)
 
3440
            self._update_revisions(stop_revision,
 
3441
                overwrite=("history" in overwrite),
 
3442
                graph=graph)
3315
3443
            # TODO: The old revid should be specified when merging tags, 
3316
3444
            # so a tags implementation that versions tags can only 
3317
3445
            # pull in the most recent changes. -- JRV20090506
3318
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3319
 
                overwrite)
 
3446
            result.tag_updates, result.tag_conflicts = (
 
3447
                self.source.tags.merge_to(self.target.tags,
 
3448
                    "tags" in overwrite,
 
3449
                    ignore_master=not merge_tags_to_master))
3320
3450
            result.new_revno, result.new_revid = self.target.last_revision_info()
3321
3451
            if _hook_master:
3322
3452
                result.master_branch = _hook_master
3331
3461
            self.source.unlock()
3332
3462
        return result
3333
3463
 
3334
 
    def push(self, overwrite=False, stop_revision=None,
3335
 
             _override_hook_source_branch=None):
3336
 
        """See InterBranch.push.
3337
 
 
3338
 
        This is the basic concrete implementation of push()
3339
 
 
3340
 
        :param _override_hook_source_branch: If specified, run
3341
 
        the hooks passing this Branch as the source, rather than self.
3342
 
        This is for use of RemoteBranch, where push is delegated to the
3343
 
        underlying vfs-based Branch.
3344
 
        """
3345
 
        # TODO: Public option to disable running hooks - should be trivial but
3346
 
        # needs tests.
3347
 
        self.source.lock_read()
3348
 
        try:
3349
 
            return _run_with_write_locked_target(
3350
 
                self.target, self._push_with_bound_branches, overwrite,
3351
 
                stop_revision,
3352
 
                _override_hook_source_branch=_override_hook_source_branch)
3353
 
        finally:
3354
 
            self.source.unlock()
3355
 
 
3356
 
    def _push_with_bound_branches(self, overwrite, stop_revision,
3357
 
            _override_hook_source_branch=None):
3358
 
        """Push from source into target, and into target's master if any.
3359
 
        """
3360
 
        def _run_hooks():
3361
 
            if _override_hook_source_branch:
3362
 
                result.source_branch = _override_hook_source_branch
3363
 
            for hook in Branch.hooks['post_push']:
3364
 
                hook(result)
3365
 
 
3366
 
        bound_location = self.target.get_bound_location()
3367
 
        if bound_location and self.target.base != bound_location:
3368
 
            # there is a master branch.
3369
 
            #
3370
 
            # XXX: Why the second check?  Is it even supported for a branch to
3371
 
            # be bound to itself? -- mbp 20070507
3372
 
            master_branch = self.target.get_master_branch()
3373
 
            master_branch.lock_write()
3374
 
            try:
3375
 
                # push into the master from the source branch.
3376
 
                self.source._basic_push(master_branch, overwrite, stop_revision)
3377
 
                # and push into the target branch from the source. Note that we
3378
 
                # push from the source branch again, because its considered the
3379
 
                # highest bandwidth repository.
3380
 
                result = self.source._basic_push(self.target, overwrite,
3381
 
                    stop_revision)
3382
 
                result.master_branch = master_branch
3383
 
                result.local_branch = self.target
3384
 
                _run_hooks()
3385
 
                return result
3386
 
            finally:
3387
 
                master_branch.unlock()
3388
 
        else:
3389
 
            # no master branch
3390
 
            result = self.source._basic_push(self.target, overwrite,
3391
 
                stop_revision)
3392
 
            # TODO: Why set master_branch and local_branch if there's no
3393
 
            # binding?  Maybe cleaner to just leave them unset? -- mbp
3394
 
            # 20070504
3395
 
            result.master_branch = self.target
3396
 
            result.local_branch = None
3397
 
            _run_hooks()
3398
 
            return result
3399
 
 
3400
 
    @classmethod
3401
 
    def is_compatible(self, source, target):
3402
 
        # GenericBranch uses the public API, so always compatible
3403
 
        return True
3404
 
 
3405
 
 
3406
 
class InterToBranch5(GenericInterBranch):
3407
 
 
3408
 
    @staticmethod
3409
 
    def _get_branch_formats_to_test():
3410
 
        return BranchFormat._default_format, BzrBranchFormat5()
3411
 
 
3412
 
    def pull(self, overwrite=False, stop_revision=None,
3413
 
             possible_transports=None, run_hooks=True,
3414
 
             _override_hook_target=None, local=False):
3415
 
        """Pull from source into self, updating my master if any.
3416
 
 
3417
 
        :param run_hooks: Private parameter - if false, this branch
3418
 
            is being called because it's the master of the primary branch,
3419
 
            so it should not run its hooks.
3420
 
        """
3421
 
        bound_location = self.target.get_bound_location()
3422
 
        if local and not bound_location:
3423
 
            raise errors.LocalRequiresBoundBranch()
3424
 
        master_branch = None
3425
 
        if not local and bound_location and self.source.user_url != bound_location:
3426
 
            # not pulling from master, so we need to update master.
3427
 
            master_branch = self.target.get_master_branch(possible_transports)
3428
 
            master_branch.lock_write()
3429
 
        try:
3430
 
            if master_branch:
3431
 
                # pull from source into master.
3432
 
                master_branch.pull(self.source, overwrite, stop_revision,
3433
 
                    run_hooks=False)
3434
 
            return super(InterToBranch5, self).pull(overwrite,
3435
 
                stop_revision, _hook_master=master_branch,
3436
 
                run_hooks=run_hooks,
3437
 
                _override_hook_target=_override_hook_target)
3438
 
        finally:
3439
 
            if master_branch:
3440
 
                master_branch.unlock()
3441
 
 
3442
3464
 
3443
3465
InterBranch.register_optimiser(GenericInterBranch)
3444
 
InterBranch.register_optimiser(InterToBranch5)