~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Martin Pool
  • Date: 2011-11-29 00:35:22 UTC
  • mto: This revision was merged to the branch mainline in revision 6320.
  • Revision ID: mbp@canonical.com-20111129003522-8ki2s26327416iie
Set a timeout of 120s per test during 'make check'

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
import bzrlib.bzrdir
17
18
 
18
19
from cStringIO import StringIO
19
 
import sys
20
20
 
21
21
from bzrlib.lazy_import import lazy_import
22
22
lazy_import(globals(), """
23
 
from itertools import chain
 
23
import itertools
24
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,
 
25
    bzrdir,
 
26
    controldir,
 
27
    cache_utf8,
 
28
    cleanup,
 
29
    config as _mod_config,
 
30
    debug,
 
31
    errors,
 
32
    fetch,
 
33
    graph as _mod_graph,
 
34
    lockdir,
 
35
    lockable_files,
 
36
    remote,
 
37
    repository,
 
38
    revision as _mod_revision,
 
39
    rio,
 
40
    tag as _mod_tag,
 
41
    transport,
 
42
    ui,
 
43
    urlutils,
46
44
    )
 
45
from bzrlib.i18n import gettext, ngettext
47
46
""")
48
47
 
49
 
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
50
 
from bzrlib.hooks import HookPoint, Hooks
 
48
from bzrlib import (
 
49
    controldir,
 
50
    )
 
51
from bzrlib.decorators import (
 
52
    needs_read_lock,
 
53
    needs_write_lock,
 
54
    only_raises,
 
55
    )
 
56
from bzrlib.hooks import Hooks
51
57
from bzrlib.inter import InterObject
52
 
from bzrlib.lock import _RelockDebugMixin
 
58
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
53
59
from bzrlib import registry
54
60
from bzrlib.symbol_versioning import (
55
61
    deprecated_in,
58
64
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
59
65
 
60
66
 
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):
 
67
class Branch(controldir.ControlComponent):
67
68
    """Branch holding a history of revisions.
68
69
 
69
70
    :ivar base:
70
71
        Base directory/url of the branch; using control_url and
71
72
        control_transport is more standardized.
72
 
 
73
 
    hooks: An instance of BranchHooks.
 
73
    :ivar hooks: An instance of BranchHooks.
 
74
    :ivar _master_branch_cache: cached result of get_master_branch, see
 
75
        _clear_cached_state.
74
76
    """
75
77
    # this is really an instance variable - FIXME move it there
76
78
    # - RBC 20060112
84
86
    def user_transport(self):
85
87
        return self.bzrdir.user_transport
86
88
 
87
 
    def __init__(self, *ignored, **ignored_too):
 
89
    def __init__(self, possible_transports=None):
88
90
        self.tags = self._format.make_tags(self)
89
91
        self._revision_history_cache = None
90
92
        self._revision_id_to_revno_cache = None
91
93
        self._partial_revision_id_to_revno_cache = {}
92
94
        self._partial_revision_history_cache = []
 
95
        self._tags_bytes = None
93
96
        self._last_revision_info_cache = None
 
97
        self._master_branch_cache = None
94
98
        self._merge_sorted_revisions_cache = None
95
 
        self._open_hook()
 
99
        self._open_hook(possible_transports)
96
100
        hooks = Branch.hooks['open']
97
101
        for hook in hooks:
98
102
            hook(self)
99
103
 
100
 
    def _open_hook(self):
 
104
    def _open_hook(self, possible_transports):
101
105
        """Called by init to allow simpler extension of the base class."""
102
106
 
103
 
    def _activate_fallback_location(self, url):
 
107
    def _activate_fallback_location(self, url, possible_transports):
104
108
        """Activate the branch/repository from url as a fallback repository."""
105
 
        repo = self._get_fallback_repository(url)
 
109
        for existing_fallback_repo in self.repository._fallback_repositories:
 
110
            if existing_fallback_repo.user_url == url:
 
111
                # This fallback is already configured.  This probably only
 
112
                # happens because ControlDir.sprout is a horrible mess.  To avoid
 
113
                # confusing _unstack we don't add this a second time.
 
114
                mutter('duplicate activation of fallback %r on %r', url, self)
 
115
                return
 
116
        repo = self._get_fallback_repository(url, possible_transports)
106
117
        if repo.has_same_location(self.repository):
107
118
            raise errors.UnstackableLocationError(self.user_url, url)
108
119
        self.repository.add_fallback_repository(repo)
162
173
        For instance, if the branch is at URL/.bzr/branch,
163
174
        Branch.open(URL) -> a Branch instance.
164
175
        """
165
 
        control = bzrdir.BzrDir.open(base, _unsupported,
 
176
        control = controldir.ControlDir.open(base, _unsupported,
166
177
                                     possible_transports=possible_transports)
167
 
        return control.open_branch(unsupported=_unsupported)
 
178
        return control.open_branch(unsupported=_unsupported,
 
179
            possible_transports=possible_transports)
168
180
 
169
181
    @staticmethod
170
 
    def open_from_transport(transport, name=None, _unsupported=False):
 
182
    def open_from_transport(transport, name=None, _unsupported=False,
 
183
            possible_transports=None):
171
184
        """Open the branch rooted at transport"""
172
 
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
173
 
        return control.open_branch(name=name, unsupported=_unsupported)
 
185
        control = controldir.ControlDir.open_from_transport(transport, _unsupported)
 
186
        return control.open_branch(name=name, unsupported=_unsupported,
 
187
            possible_transports=possible_transports)
174
188
 
175
189
    @staticmethod
176
190
    def open_containing(url, possible_transports=None):
184
198
        format, UnknownFormatError or UnsupportedFormatError are raised.
185
199
        If there is one, it is returned, along with the unused portion of url.
186
200
        """
187
 
        control, relpath = bzrdir.BzrDir.open_containing(url,
 
201
        control, relpath = controldir.ControlDir.open_containing(url,
188
202
                                                         possible_transports)
189
 
        return control.open_branch(), relpath
 
203
        branch = control.open_branch(possible_transports=possible_transports)
 
204
        return (branch, relpath)
190
205
 
191
206
    def _push_should_merge_tags(self):
192
207
        """Should _basic_push merge this branch's tags into the target?
197
212
        return self.supports_tags() and self.tags.get_tag_dict()
198
213
 
199
214
    def get_config(self):
200
 
        return BranchConfig(self)
 
215
        """Get a bzrlib.config.BranchConfig for this Branch.
 
216
 
 
217
        This can then be used to get and set configuration options for the
 
218
        branch.
 
219
 
 
220
        :return: A bzrlib.config.BranchConfig.
 
221
        """
 
222
        return _mod_config.BranchConfig(self)
 
223
 
 
224
    def get_config_stack(self):
 
225
        """Get a bzrlib.config.BranchStack for this Branch.
 
226
 
 
227
        This can then be used to get and set configuration options for the
 
228
        branch.
 
229
 
 
230
        :return: A bzrlib.config.BranchStack.
 
231
        """
 
232
        return _mod_config.BranchStack(self)
201
233
 
202
234
    def _get_config(self):
203
235
        """Get the concrete config for just the config in this branch.
211
243
        """
212
244
        raise NotImplementedError(self._get_config)
213
245
 
214
 
    def _get_fallback_repository(self, url):
 
246
    def _get_fallback_repository(self, url, possible_transports):
215
247
        """Get the repository we fallback to at url."""
216
248
        url = urlutils.join(self.base, url)
217
 
        a_branch = Branch.open(url,
218
 
            possible_transports=[self.bzrdir.root_transport])
 
249
        a_branch = Branch.open(url, possible_transports=possible_transports)
219
250
        return a_branch.repository
220
251
 
 
252
    @needs_read_lock
221
253
    def _get_tags_bytes(self):
222
254
        """Get the bytes of a serialised tags dict.
223
255
 
230
262
        :return: The bytes of the tags file.
231
263
        :seealso: Branch._set_tags_bytes.
232
264
        """
233
 
        return self._transport.get_bytes('tags')
 
265
        if self._tags_bytes is None:
 
266
            self._tags_bytes = self._transport.get_bytes('tags')
 
267
        return self._tags_bytes
234
268
 
235
269
    def _get_nick(self, local=False, possible_transports=None):
236
270
        config = self.get_config()
238
272
        if not local and not config.has_explicit_nickname():
239
273
            try:
240
274
                master = self.get_master_branch(possible_transports)
 
275
                if master and self.user_url == master.user_url:
 
276
                    raise errors.RecursiveBind(self.user_url)
241
277
                if master is not None:
242
278
                    # return the master branch value
243
279
                    return master.nick
 
280
            except errors.RecursiveBind, e:
 
281
                raise e
244
282
            except errors.BzrError, e:
245
283
                # Silently fall back to local implicit nick if the master is
246
284
                # unavailable
283
321
        new_history.reverse()
284
322
        return new_history
285
323
 
286
 
    def lock_write(self):
 
324
    def lock_write(self, token=None):
 
325
        """Lock the branch for write operations.
 
326
 
 
327
        :param token: A token to permit reacquiring a previously held and
 
328
            preserved lock.
 
329
        :return: A BranchWriteLockResult.
 
330
        """
287
331
        raise NotImplementedError(self.lock_write)
288
332
 
289
333
    def lock_read(self):
 
334
        """Lock the branch for read operations.
 
335
 
 
336
        :return: A bzrlib.lock.LogicalLockResult.
 
337
        """
290
338
        raise NotImplementedError(self.lock_read)
291
339
 
292
340
    def unlock(self):
413
461
            after. If None, the rest of history is included.
414
462
        :param stop_rule: if stop_revision_id is not None, the precise rule
415
463
            to use for termination:
 
464
 
416
465
            * 'exclude' - leave the stop revision out of the result (default)
417
466
            * 'include' - the stop revision is the last item in the result
418
467
            * 'with-merges' - include the stop revision and all of its
420
469
            * 'with-merges-without-common-ancestry' - filter out revisions 
421
470
              that are in both ancestries
422
471
        :param direction: either 'reverse' or 'forward':
 
472
 
423
473
            * reverse means return the start_revision_id first, i.e.
424
474
              start at the most recent revision and go backwards in history
425
475
            * forward returns tuples in the opposite order to reverse.
469
519
        rev_iter = iter(merge_sorted_revisions)
470
520
        if start_revision_id is not None:
471
521
            for node in rev_iter:
472
 
                rev_id = node.key[-1]
 
522
                rev_id = node.key
473
523
                if rev_id != start_revision_id:
474
524
                    continue
475
525
                else:
476
526
                    # The decision to include the start or not
477
527
                    # depends on the stop_rule if a stop is provided
478
528
                    # so pop this node back into the iterator
479
 
                    rev_iter = chain(iter([node]), rev_iter)
 
529
                    rev_iter = itertools.chain(iter([node]), rev_iter)
480
530
                    break
481
531
        if stop_revision_id is None:
482
532
            # Yield everything
483
533
            for node in rev_iter:
484
 
                rev_id = node.key[-1]
 
534
                rev_id = node.key
485
535
                yield (rev_id, node.merge_depth, node.revno,
486
536
                       node.end_of_merge)
487
537
        elif stop_rule == 'exclude':
488
538
            for node in rev_iter:
489
 
                rev_id = node.key[-1]
 
539
                rev_id = node.key
490
540
                if rev_id == stop_revision_id:
491
541
                    return
492
542
                yield (rev_id, node.merge_depth, node.revno,
493
543
                       node.end_of_merge)
494
544
        elif stop_rule == 'include':
495
545
            for node in rev_iter:
496
 
                rev_id = node.key[-1]
 
546
                rev_id = node.key
497
547
                yield (rev_id, node.merge_depth, node.revno,
498
548
                       node.end_of_merge)
499
549
                if rev_id == stop_revision_id:
505
555
            ancestors = graph.find_unique_ancestors(start_revision_id,
506
556
                                                    [stop_revision_id])
507
557
            for node in rev_iter:
508
 
                rev_id = node.key[-1]
 
558
                rev_id = node.key
509
559
                if rev_id not in ancestors:
510
560
                    continue
511
561
                yield (rev_id, node.merge_depth, node.revno,
521
571
            reached_stop_revision_id = False
522
572
            revision_id_whitelist = []
523
573
            for node in rev_iter:
524
 
                rev_id = node.key[-1]
 
574
                rev_id = node.key
525
575
                if rev_id == left_parent:
526
576
                    # reached the left parent after the stop_revision
527
577
                    return
607
657
        """
608
658
        raise errors.UpgradeRequired(self.user_url)
609
659
 
 
660
    def get_append_revisions_only(self):
 
661
        """Whether it is only possible to append revisions to the history.
 
662
        """
 
663
        if not self._format.supports_set_append_revisions_only():
 
664
            return False
 
665
        return self.get_config(
 
666
            ).get_user_option_as_bool('append_revisions_only')
 
667
 
610
668
    def set_append_revisions_only(self, enabled):
611
669
        if not self._format.supports_set_append_revisions_only():
612
670
            raise errors.UpgradeRequired(self.user_url)
626
684
        raise errors.UnsupportedOperation(self.get_reference_info, self)
627
685
 
628
686
    @needs_write_lock
629
 
    def fetch(self, from_branch, last_revision=None, pb=None):
 
687
    def fetch(self, from_branch, last_revision=None, limit=None):
630
688
        """Copy revisions from from_branch into this branch.
631
689
 
632
690
        :param from_branch: Where to copy from.
633
691
        :param last_revision: What revision to stop at (None for at the end
634
692
                              of the branch.
635
 
        :param pb: An optional progress bar to use.
 
693
        :param limit: Optional rough limit of revisions to fetch
636
694
        :return: None
637
695
        """
638
 
        if self.base == from_branch.base:
639
 
            return (0, [])
640
 
        if pb is not None:
641
 
            symbol_versioning.warn(
642
 
                symbol_versioning.deprecated_in((1, 14, 0))
643
 
                % "pb parameter to fetch()")
644
 
        from_branch.lock_read()
645
 
        try:
646
 
            if last_revision is None:
647
 
                last_revision = from_branch.last_revision()
648
 
                last_revision = _mod_revision.ensure_null(last_revision)
649
 
            return self.repository.fetch(from_branch.repository,
650
 
                                         revision_id=last_revision,
651
 
                                         pb=pb)
652
 
        finally:
653
 
            from_branch.unlock()
 
696
        return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
654
697
 
655
698
    def get_bound_location(self):
656
699
        """Return the URL of the branch we are bound to.
667
710
 
668
711
    def get_commit_builder(self, parents, config=None, timestamp=None,
669
712
                           timezone=None, committer=None, revprops=None,
670
 
                           revision_id=None):
 
713
                           revision_id=None, lossy=False):
671
714
        """Obtain a CommitBuilder for this branch.
672
715
 
673
716
        :param parents: Revision ids of the parents of the new revision.
677
720
        :param committer: Optional committer to set for commit.
678
721
        :param revprops: Optional dictionary of revision properties.
679
722
        :param revision_id: Optional revision id.
 
723
        :param lossy: Whether to discard data that can not be natively
 
724
            represented, when pushing to a foreign VCS 
680
725
        """
681
726
 
682
727
        if config is None:
683
728
            config = self.get_config()
684
729
 
685
730
        return self.repository.get_commit_builder(self, parents, config,
686
 
            timestamp, timezone, committer, revprops, revision_id)
 
731
            timestamp, timezone, committer, revprops, revision_id,
 
732
            lossy)
687
733
 
688
734
    def get_master_branch(self, possible_transports=None):
689
735
        """Return the branch we are bound to.
692
738
        """
693
739
        return None
694
740
 
 
741
    @deprecated_method(deprecated_in((2, 5, 0)))
695
742
    def get_revision_delta(self, revno):
696
743
        """Return the delta for one revision.
697
744
 
698
745
        The delta is relative to its mainline predecessor, or the
699
746
        empty tree for revision 1.
700
747
        """
701
 
        rh = self.revision_history()
702
 
        if not (1 <= revno <= len(rh)):
 
748
        try:
 
749
            revid = self.get_rev_id(revno)
 
750
        except errors.NoSuchRevision:
703
751
            raise errors.InvalidRevisionNumber(revno)
704
 
        return self.repository.get_revision_delta(rh[revno-1])
 
752
        return self.repository.get_revision_delta(revid)
705
753
 
706
754
    def get_stacked_on_url(self):
707
755
        """Get the URL this branch is stacked against.
716
764
        """Print `file` to stdout."""
717
765
        raise NotImplementedError(self.print_file)
718
766
 
 
767
    @deprecated_method(deprecated_in((2, 4, 0)))
719
768
    def set_revision_history(self, rev_history):
720
 
        raise NotImplementedError(self.set_revision_history)
 
769
        """See Branch.set_revision_history."""
 
770
        self._set_revision_history(rev_history)
 
771
 
 
772
    @needs_write_lock
 
773
    def _set_revision_history(self, rev_history):
 
774
        if len(rev_history) == 0:
 
775
            revid = _mod_revision.NULL_REVISION
 
776
        else:
 
777
            revid = rev_history[-1]
 
778
        if rev_history != self._lefthand_history(revid):
 
779
            raise errors.NotLefthandHistory(rev_history)
 
780
        self.set_last_revision_info(len(rev_history), revid)
 
781
        self._cache_revision_history(rev_history)
 
782
        for hook in Branch.hooks['set_rh']:
 
783
            hook(self, rev_history)
 
784
 
 
785
    @needs_write_lock
 
786
    def set_last_revision_info(self, revno, revision_id):
 
787
        """Set the last revision of this branch.
 
788
 
 
789
        The caller is responsible for checking that the revno is correct
 
790
        for this revision id.
 
791
 
 
792
        It may be possible to set the branch last revision to an id not
 
793
        present in the repository.  However, branches can also be
 
794
        configured to check constraints on history, in which case this may not
 
795
        be permitted.
 
796
        """
 
797
        raise NotImplementedError(self.set_last_revision_info)
 
798
 
 
799
    @needs_write_lock
 
800
    def generate_revision_history(self, revision_id, last_rev=None,
 
801
                                  other_branch=None):
 
802
        """See Branch.generate_revision_history"""
 
803
        graph = self.repository.get_graph()
 
804
        (last_revno, last_revid) = self.last_revision_info()
 
805
        known_revision_ids = [
 
806
            (last_revid, last_revno),
 
807
            (_mod_revision.NULL_REVISION, 0),
 
808
            ]
 
809
        if last_rev is not None:
 
810
            if not graph.is_ancestor(last_rev, revision_id):
 
811
                # our previous tip is not merged into stop_revision
 
812
                raise errors.DivergedBranches(self, other_branch)
 
813
        revno = graph.find_distance_to_null(revision_id, known_revision_ids)
 
814
        self.set_last_revision_info(revno, revision_id)
721
815
 
722
816
    @needs_write_lock
723
817
    def set_parent(self, url):
760
854
                return
761
855
            self._unstack()
762
856
        else:
763
 
            self._activate_fallback_location(url)
 
857
            self._activate_fallback_location(url,
 
858
                possible_transports=[self.bzrdir.root_transport])
764
859
        # write this out after the repository is stacked to avoid setting a
765
860
        # stacked config that doesn't work.
766
861
        self._set_config_location('stacked_on_location', url)
767
862
 
768
863
    def _unstack(self):
769
864
        """Change a branch to be unstacked, copying data as needed.
770
 
        
 
865
 
771
866
        Don't call this directly, use set_stacked_on_url(None).
772
867
        """
773
868
        pb = ui.ui_factory.nested_progress_bar()
774
869
        try:
775
 
            pb.update("Unstacking")
 
870
            pb.update(gettext("Unstacking"))
776
871
            # The basic approach here is to fetch the tip of the branch,
777
872
            # including all available ghosts, from the existing stacked
778
873
            # repository into a new repository object without the fallbacks. 
782
877
            old_repository = self.repository
783
878
            if len(old_repository._fallback_repositories) != 1:
784
879
                raise AssertionError("can't cope with fallback repositories "
785
 
                    "of %r" % (self.repository,))
786
 
            # unlock it, including unlocking the fallback
 
880
                    "of %r (fallbacks: %r)" % (old_repository,
 
881
                        old_repository._fallback_repositories))
 
882
            # Open the new repository object.
 
883
            # Repositories don't offer an interface to remove fallback
 
884
            # repositories today; take the conceptually simpler option and just
 
885
            # reopen it.  We reopen it starting from the URL so that we
 
886
            # get a separate connection for RemoteRepositories and can
 
887
            # stream from one of them to the other.  This does mean doing
 
888
            # separate SSH connection setup, but unstacking is not a
 
889
            # common operation so it's tolerable.
 
890
            new_bzrdir = controldir.ControlDir.open(
 
891
                self.bzrdir.root_transport.base)
 
892
            new_repository = new_bzrdir.find_repository()
 
893
            if new_repository._fallback_repositories:
 
894
                raise AssertionError("didn't expect %r to have "
 
895
                    "fallback_repositories"
 
896
                    % (self.repository,))
 
897
            # Replace self.repository with the new repository.
 
898
            # Do our best to transfer the lock state (i.e. lock-tokens and
 
899
            # lock count) of self.repository to the new repository.
 
900
            lock_token = old_repository.lock_write().repository_token
 
901
            self.repository = new_repository
 
902
            if isinstance(self, remote.RemoteBranch):
 
903
                # Remote branches can have a second reference to the old
 
904
                # repository that need to be replaced.
 
905
                if self._real_branch is not None:
 
906
                    self._real_branch.repository = new_repository
 
907
            self.repository.lock_write(token=lock_token)
 
908
            if lock_token is not None:
 
909
                old_repository.leave_lock_in_place()
787
910
            old_repository.unlock()
 
911
            if lock_token is not None:
 
912
                # XXX: self.repository.leave_lock_in_place() before this
 
913
                # function will not be preserved.  Fortunately that doesn't
 
914
                # affect the current default format (2a), and would be a
 
915
                # corner-case anyway.
 
916
                #  - Andrew Bennetts, 2010/06/30
 
917
                self.repository.dont_leave_lock_in_place()
 
918
            old_lock_count = 0
 
919
            while True:
 
920
                try:
 
921
                    old_repository.unlock()
 
922
                except errors.LockNotHeld:
 
923
                    break
 
924
                old_lock_count += 1
 
925
            if old_lock_count == 0:
 
926
                raise AssertionError(
 
927
                    'old_repository should have been locked at least once.')
 
928
            for i in range(old_lock_count-1):
 
929
                self.repository.lock_write()
 
930
            # Fetch from the old repository into the new.
788
931
            old_repository.lock_read()
789
932
            try:
790
 
                # Repositories don't offer an interface to remove fallback
791
 
                # repositories today; take the conceptually simpler option and just
792
 
                # reopen it.  We reopen it starting from the URL so that we
793
 
                # get a separate connection for RemoteRepositories and can
794
 
                # stream from one of them to the other.  This does mean doing
795
 
                # separate SSH connection setup, but unstacking is not a
796
 
                # common operation so it's tolerable.
797
 
                new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
798
 
                new_repository = new_bzrdir.find_repository()
799
 
                self.repository = new_repository
800
 
                if self.repository._fallback_repositories:
801
 
                    raise AssertionError("didn't expect %r to have "
802
 
                        "fallback_repositories"
803
 
                        % (self.repository,))
804
 
                # this is not paired with an unlock because it's just restoring
805
 
                # the previous state; the lock's released when set_stacked_on_url
806
 
                # returns
807
 
                self.repository.lock_write()
808
933
                # XXX: If you unstack a branch while it has a working tree
809
934
                # with a pending merge, the pending-merged revisions will no
810
935
                # longer be present.  You can (probably) revert and remerge.
811
 
                #
812
 
                # XXX: This only fetches up to the tip of the repository; it
813
 
                # doesn't bring across any tags.  That's fairly consistent
814
 
                # with how branch works, but perhaps not ideal.
815
 
                self.repository.fetch(old_repository,
816
 
                    revision_id=self.last_revision(),
817
 
                    find_ghosts=True)
 
936
                try:
 
937
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
 
938
                except errors.TagsNotSupported:
 
939
                    tags_to_fetch = set()
 
940
                fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
 
941
                    old_repository, required_ids=[self.last_revision()],
 
942
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
 
943
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
818
944
            finally:
819
945
                old_repository.unlock()
820
946
        finally:
825
951
 
826
952
        :seealso: Branch._get_tags_bytes.
827
953
        """
828
 
        return _run_with_write_locked_target(self, self._transport.put_bytes,
829
 
            'tags', bytes)
 
954
        op = cleanup.OperationWithCleanups(self._set_tags_bytes_locked)
 
955
        op.add_cleanup(self.lock_write().unlock)
 
956
        return op.run_simple(bytes)
 
957
 
 
958
    def _set_tags_bytes_locked(self, bytes):
 
959
        self._tags_bytes = bytes
 
960
        return self._transport.put_bytes('tags', bytes)
830
961
 
831
962
    def _cache_revision_history(self, rev_history):
832
963
        """Set the cached revision history to rev_history.
859
990
        self._revision_history_cache = None
860
991
        self._revision_id_to_revno_cache = None
861
992
        self._last_revision_info_cache = None
 
993
        self._master_branch_cache = None
862
994
        self._merge_sorted_revisions_cache = None
863
995
        self._partial_revision_history_cache = []
864
996
        self._partial_revision_id_to_revno_cache = {}
 
997
        self._tags_bytes = None
865
998
 
866
999
    def _gen_revision_history(self):
867
1000
        """Return sequence of revision hashes on to this branch.
878
1011
        """
879
1012
        raise NotImplementedError(self._gen_revision_history)
880
1013
 
 
1014
    @deprecated_method(deprecated_in((2, 5, 0)))
881
1015
    @needs_read_lock
882
1016
    def revision_history(self):
883
1017
        """Return sequence of revision ids on this branch.
885
1019
        This method will cache the revision history for as long as it is safe to
886
1020
        do so.
887
1021
        """
 
1022
        return self._revision_history()
 
1023
 
 
1024
    def _revision_history(self):
888
1025
        if 'evil' in debug.debug_flags:
889
1026
            mutter_callsite(3, "revision_history scales with history.")
890
1027
        if self._revision_history_cache is not None:
917
1054
        :return: A tuple (revno, revision_id).
918
1055
        """
919
1056
        if self._last_revision_info_cache is None:
920
 
            self._last_revision_info_cache = self._last_revision_info()
 
1057
            self._last_revision_info_cache = self._read_last_revision_info()
921
1058
        return self._last_revision_info_cache
922
1059
 
923
 
    def _last_revision_info(self):
924
 
        rh = self.revision_history()
925
 
        revno = len(rh)
926
 
        if revno:
927
 
            return (revno, rh[-1])
928
 
        else:
929
 
            return (0, _mod_revision.NULL_REVISION)
930
 
 
931
 
    @deprecated_method(deprecated_in((1, 6, 0)))
932
 
    def missing_revisions(self, other, stop_revision=None):
933
 
        """Return a list of new revisions that would perfectly fit.
934
 
 
935
 
        If self and other have not diverged, return a list of the revisions
936
 
        present in other, but missing from self.
937
 
        """
938
 
        self_history = self.revision_history()
939
 
        self_len = len(self_history)
940
 
        other_history = other.revision_history()
941
 
        other_len = len(other_history)
942
 
        common_index = min(self_len, other_len) -1
943
 
        if common_index >= 0 and \
944
 
            self_history[common_index] != other_history[common_index]:
945
 
            raise errors.DivergedBranches(self, other)
946
 
 
947
 
        if stop_revision is None:
948
 
            stop_revision = other_len
949
 
        else:
950
 
            if stop_revision > other_len:
951
 
                raise errors.NoSuchRevision(self, stop_revision)
952
 
        return other_history[self_len:stop_revision]
953
 
 
954
 
    @needs_write_lock
955
 
    def update_revisions(self, other, stop_revision=None, overwrite=False,
956
 
                         graph=None):
957
 
        """Pull in new perfect-fit revisions.
958
 
 
959
 
        :param other: Another Branch to pull from
960
 
        :param stop_revision: Updated until the given revision
961
 
        :param overwrite: Always set the branch pointer, rather than checking
962
 
            to see if it is a proper descendant.
963
 
        :param graph: A Graph object that can be used to query history
964
 
            information. This can be None.
965
 
        :return: None
966
 
        """
967
 
        return InterBranch.get(other, self).update_revisions(stop_revision,
968
 
            overwrite, graph)
969
 
 
 
1060
    def _read_last_revision_info(self):
 
1061
        raise NotImplementedError(self._read_last_revision_info)
 
1062
 
 
1063
    @deprecated_method(deprecated_in((2, 4, 0)))
970
1064
    def import_last_revision_info(self, source_repo, revno, revid):
971
1065
        """Set the last revision info, importing from another repo if necessary.
972
1066
 
973
 
        This is used by the bound branch code to upload a revision to
974
 
        the master branch first before updating the tip of the local branch.
975
 
 
976
1067
        :param source_repo: Source repository to optionally fetch from
977
1068
        :param revno: Revision number of the new tip
978
1069
        :param revid: Revision id of the new tip
981
1072
            self.repository.fetch(source_repo, revision_id=revid)
982
1073
        self.set_last_revision_info(revno, revid)
983
1074
 
 
1075
    def import_last_revision_info_and_tags(self, source, revno, revid,
 
1076
                                           lossy=False):
 
1077
        """Set the last revision info, importing from another repo if necessary.
 
1078
 
 
1079
        This is used by the bound branch code to upload a revision to
 
1080
        the master branch first before updating the tip of the local branch.
 
1081
        Revisions referenced by source's tags are also transferred.
 
1082
 
 
1083
        :param source: Source branch to optionally fetch from
 
1084
        :param revno: Revision number of the new tip
 
1085
        :param revid: Revision id of the new tip
 
1086
        :param lossy: Whether to discard metadata that can not be
 
1087
            natively represented
 
1088
        :return: Tuple with the new revision number and revision id
 
1089
            (should only be different from the arguments when lossy=True)
 
1090
        """
 
1091
        if not self.repository.has_same_location(source.repository):
 
1092
            self.fetch(source, revid)
 
1093
        self.set_last_revision_info(revno, revid)
 
1094
        return (revno, revid)
 
1095
 
984
1096
    def revision_id_to_revno(self, revision_id):
985
1097
        """Given a revision id, return its revno"""
986
1098
        if _mod_revision.is_null(revision_id):
987
1099
            return 0
988
 
        history = self.revision_history()
 
1100
        history = self._revision_history()
989
1101
        try:
990
1102
            return history.index(revision_id) + 1
991
1103
        except ValueError:
1006
1118
            self._extend_partial_history(distance_from_last)
1007
1119
        return self._partial_revision_history_cache[distance_from_last]
1008
1120
 
1009
 
    @needs_write_lock
1010
1121
    def pull(self, source, overwrite=False, stop_revision=None,
1011
1122
             possible_transports=None, *args, **kwargs):
1012
1123
        """Mirror source into this branch.
1019
1130
            stop_revision=stop_revision,
1020
1131
            possible_transports=possible_transports, *args, **kwargs)
1021
1132
 
1022
 
    def push(self, target, overwrite=False, stop_revision=None, *args,
1023
 
        **kwargs):
 
1133
    def push(self, target, overwrite=False, stop_revision=None, lossy=False,
 
1134
            *args, **kwargs):
1024
1135
        """Mirror this branch into target.
1025
1136
 
1026
1137
        This branch is considered to be 'local', having low latency.
1027
1138
        """
1028
1139
        return InterBranch.get(self, target).push(overwrite, stop_revision,
1029
 
            *args, **kwargs)
1030
 
 
1031
 
    def lossy_push(self, target, stop_revision=None):
1032
 
        """Push deltas into another branch.
1033
 
 
1034
 
        :note: This does not, like push, retain the revision ids from 
1035
 
            the source branch and will, rather than adding bzr-specific 
1036
 
            metadata, push only those semantics of the revision that can be 
1037
 
            natively represented by this branch' VCS.
1038
 
 
1039
 
        :param target: Target branch
1040
 
        :param stop_revision: Revision to push, defaults to last revision.
1041
 
        :return: BranchPushResult with an extra member revidmap: 
1042
 
            A dictionary mapping revision ids from the target branch 
1043
 
            to new revision ids in the target branch, for each 
1044
 
            revision that was pushed.
1045
 
        """
1046
 
        inter = InterBranch.get(self, target)
1047
 
        lossy_push = getattr(inter, "lossy_push", None)
1048
 
        if lossy_push is None:
1049
 
            raise errors.LossyPushToSameVCS(self, target)
1050
 
        return lossy_push(stop_revision)
 
1140
            lossy, *args, **kwargs)
1051
1141
 
1052
1142
    def basis_tree(self):
1053
1143
        """Return `Tree` object for last revision."""
1208
1298
        return result
1209
1299
 
1210
1300
    @needs_read_lock
1211
 
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
 
1301
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
 
1302
            repository=None):
1212
1303
        """Create a new line of development from the branch, into to_bzrdir.
1213
1304
 
1214
1305
        to_bzrdir controls the branch format.
1219
1310
        if (repository_policy is not None and
1220
1311
            repository_policy.requires_stacking()):
1221
1312
            to_bzrdir._format.require_stacking(_skip_repo=True)
1222
 
        result = to_bzrdir.create_branch()
 
1313
        result = to_bzrdir.create_branch(repository=repository)
1223
1314
        result.lock_write()
1224
1315
        try:
1225
1316
            if repository_policy is not None:
1226
1317
                repository_policy.configure_branch(result)
1227
1318
            self.copy_content_into(result, revision_id=revision_id)
1228
 
            result.set_parent(self.bzrdir.root_transport.base)
 
1319
            master_url = self.get_bound_location()
 
1320
            if master_url is None:
 
1321
                result.set_parent(self.bzrdir.root_transport.base)
 
1322
            else:
 
1323
                result.set_parent(master_url)
1229
1324
        finally:
1230
1325
            result.unlock()
1231
1326
        return result
1255
1350
                revno = 1
1256
1351
        destination.set_last_revision_info(revno, revision_id)
1257
1352
 
1258
 
    @needs_read_lock
1259
1353
    def copy_content_into(self, destination, revision_id=None):
1260
1354
        """Copy the content of self into destination.
1261
1355
 
1262
1356
        revision_id: if not None, the revision history in the new branch will
1263
1357
                     be truncated to end with revision_id.
1264
1358
        """
1265
 
        self.update_references(destination)
1266
 
        self._synchronize_history(destination, revision_id)
1267
 
        try:
1268
 
            parent = self.get_parent()
1269
 
        except errors.InaccessibleParent, e:
1270
 
            mutter('parent was not accessible to copy: %s', e)
1271
 
        else:
1272
 
            if parent:
1273
 
                destination.set_parent(parent)
1274
 
        if self._push_should_merge_tags():
1275
 
            self.tags.merge_to(destination.tags)
 
1359
        return InterBranch.get(self, destination).copy_content_into(
 
1360
            revision_id=revision_id)
1276
1361
 
1277
1362
    def update_references(self, target):
1278
1363
        if not getattr(self._format, 'supports_reference_locations', False):
1315
1400
        # TODO: We should probably also check that self.revision_history
1316
1401
        # matches the repository for older branch formats.
1317
1402
        # If looking for the code that cross-checks repository parents against
1318
 
        # the iter_reverse_revision_history output, that is now a repository
 
1403
        # the Graph.iter_lefthand_ancestry output, that is now a repository
1319
1404
        # specific check.
1320
1405
        return result
1321
1406
 
1322
 
    def _get_checkout_format(self):
 
1407
    def _get_checkout_format(self, lightweight=False):
1323
1408
        """Return the most suitable metadir for a checkout of this branch.
1324
1409
        Weaves are used if this branch's repository uses weaves.
1325
1410
        """
1326
 
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
1327
 
            from bzrlib.repofmt import weaverepo
1328
 
            format = bzrdir.BzrDirMetaFormat1()
1329
 
            format.repository_format = weaverepo.RepositoryFormat7()
1330
 
        else:
1331
 
            format = self.repository.bzrdir.checkout_metadir()
1332
 
            format.set_branch_format(self._format)
 
1411
        format = self.repository.bzrdir.checkout_metadir()
 
1412
        format.set_branch_format(self._format)
1333
1413
        return format
1334
1414
 
1335
1415
    def create_clone_on_transport(self, to_transport, revision_id=None,
1336
 
        stacked_on=None, create_prefix=False, use_existing_dir=False):
 
1416
        stacked_on=None, create_prefix=False, use_existing_dir=False,
 
1417
        no_tree=None):
1337
1418
        """Create a clone of this branch and its bzrdir.
1338
1419
 
1339
1420
        :param to_transport: The transport to clone onto.
1346
1427
        """
1347
1428
        # XXX: Fix the bzrdir API to allow getting the branch back from the
1348
1429
        # clone call. Or something. 20090224 RBC/spiv.
 
1430
        # XXX: Should this perhaps clone colocated branches as well, 
 
1431
        # rather than just the default branch? 20100319 JRV
1349
1432
        if revision_id is None:
1350
1433
            revision_id = self.last_revision()
1351
1434
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1352
1435
            revision_id=revision_id, stacked_on=stacked_on,
1353
 
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
 
1436
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
 
1437
            no_tree=no_tree)
1354
1438
        return dir_to.open_branch()
1355
1439
 
1356
1440
    def create_checkout(self, to_location, revision_id=None,
1361
1445
        :param to_location: The url to produce the checkout at
1362
1446
        :param revision_id: The revision to check out
1363
1447
        :param lightweight: If True, produce a lightweight checkout, otherwise,
1364
 
        produce a bound branch (heavyweight checkout)
 
1448
            produce a bound branch (heavyweight checkout)
1365
1449
        :param accelerator_tree: A tree which can be used for retrieving file
1366
1450
            contents more quickly than the revision tree, i.e. a workingtree.
1367
1451
            The revision tree will be used for cases where accelerator_tree's
1372
1456
        """
1373
1457
        t = transport.get_transport(to_location)
1374
1458
        t.ensure_base()
 
1459
        format = self._get_checkout_format(lightweight=lightweight)
1375
1460
        if lightweight:
1376
 
            format = self._get_checkout_format()
1377
1461
            checkout = format.initialize_on_transport(t)
1378
1462
            from_branch = BranchReferenceFormat().initialize(checkout, 
1379
1463
                target_branch=self)
1380
1464
        else:
1381
 
            format = self._get_checkout_format()
1382
 
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
 
1465
            checkout_branch = controldir.ControlDir.create_branch_convenience(
1383
1466
                to_location, force_new_tree=False, format=format)
1384
1467
            checkout = checkout_branch.bzrdir
1385
1468
            checkout_branch.bind(self)
1413
1496
 
1414
1497
    def reference_parent(self, file_id, path, possible_transports=None):
1415
1498
        """Return the parent branch for a tree-reference file_id
 
1499
 
1416
1500
        :param file_id: The file_id of the tree reference
1417
1501
        :param path: The path of the file_id in the tree
1418
1502
        :return: A branch associated with the file_id
1471
1555
        else:
1472
1556
            raise AssertionError("invalid heads: %r" % (heads,))
1473
1557
 
1474
 
 
1475
 
class BranchFormat(object):
 
1558
    def heads_to_fetch(self):
 
1559
        """Return the heads that must and that should be fetched to copy this
 
1560
        branch into another repo.
 
1561
 
 
1562
        :returns: a 2-tuple of (must_fetch, if_present_fetch).  must_fetch is a
 
1563
            set of heads that must be fetched.  if_present_fetch is a set of
 
1564
            heads that must be fetched if present, but no error is necessary if
 
1565
            they are not present.
 
1566
        """
 
1567
        # For bzr native formats must_fetch is just the tip, and if_present_fetch
 
1568
        # are the tags.
 
1569
        must_fetch = set([self.last_revision()])
 
1570
        if_present_fetch = set()
 
1571
        c = self.get_config()
 
1572
        include_tags = c.get_user_option_as_bool('branch.fetch_tags',
 
1573
                                                 default=False)
 
1574
        if include_tags:
 
1575
            try:
 
1576
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
 
1577
            except errors.TagsNotSupported:
 
1578
                pass
 
1579
        must_fetch.discard(_mod_revision.NULL_REVISION)
 
1580
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
 
1581
        return must_fetch, if_present_fetch
 
1582
 
 
1583
 
 
1584
class BranchFormat(controldir.ControlComponentFormat):
1476
1585
    """An encapsulation of the initialization and open routines for a format.
1477
1586
 
1478
1587
    Formats provide three things:
1481
1590
     * an open routine.
1482
1591
 
1483
1592
    Formats are placed in an dict by their format string for reference
1484
 
    during branch opening. Its not required that these be instances, they
 
1593
    during branch opening. It's not required that these be instances, they
1485
1594
    can be classes themselves with class methods - it simply depends on
1486
1595
    whether state is needed for a given format or not.
1487
1596
 
1490
1599
    object will be created every time regardless.
1491
1600
    """
1492
1601
 
1493
 
    _default_format = None
1494
 
    """The default format used for new branches."""
1495
 
 
1496
 
    _formats = {}
1497
 
    """The known formats."""
1498
 
 
1499
 
    can_set_append_revisions_only = True
1500
 
 
1501
1602
    def __eq__(self, other):
1502
1603
        return self.__class__ is other.__class__
1503
1604
 
1505
1606
        return not (self == other)
1506
1607
 
1507
1608
    @classmethod
1508
 
    def find_format(klass, a_bzrdir, name=None):
1509
 
        """Return the format for the branch object in a_bzrdir."""
1510
 
        try:
1511
 
            transport = a_bzrdir.get_branch_transport(None, name=name)
 
1609
    def find_format(klass, controldir, name=None):
 
1610
        """Return the format for the branch object in controldir."""
 
1611
        try:
 
1612
            transport = controldir.get_branch_transport(None, name=name)
 
1613
        except errors.NoSuchFile:
 
1614
            raise errors.NotBranchError(path=name, bzrdir=controldir)
 
1615
        try:
1512
1616
            format_string = transport.get_bytes("format")
1513
 
            return klass._formats[format_string]
 
1617
            return format_registry.get(format_string)
1514
1618
        except errors.NoSuchFile:
1515
 
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
 
1619
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1516
1620
        except KeyError:
1517
1621
            raise errors.UnknownFormatError(format=format_string, kind='branch')
1518
1622
 
1519
1623
    @classmethod
 
1624
    @deprecated_method(deprecated_in((2, 4, 0)))
1520
1625
    def get_default_format(klass):
1521
1626
        """Return the current default format."""
1522
 
        return klass._default_format
1523
 
 
1524
 
    def get_reference(self, a_bzrdir):
1525
 
        """Get the target reference of the branch in a_bzrdir.
 
1627
        return format_registry.get_default()
 
1628
 
 
1629
    @classmethod
 
1630
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1631
    def get_formats(klass):
 
1632
        """Get all the known formats.
 
1633
 
 
1634
        Warning: This triggers a load of all lazy registered formats: do not
 
1635
        use except when that is desireed.
 
1636
        """
 
1637
        return format_registry._get_all()
 
1638
 
 
1639
    def get_reference(self, controldir, name=None):
 
1640
        """Get the target reference of the branch in controldir.
1526
1641
 
1527
1642
        format probing must have been completed before calling
1528
1643
        this method - it is assumed that the format of the branch
1529
 
        in a_bzrdir is correct.
 
1644
        in controldir is correct.
1530
1645
 
1531
 
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1646
        :param controldir: The controldir to get the branch data from.
 
1647
        :param name: Name of the colocated branch to fetch
1532
1648
        :return: None if the branch is not a reference branch.
1533
1649
        """
1534
1650
        return None
1535
1651
 
1536
1652
    @classmethod
1537
 
    def set_reference(self, a_bzrdir, to_branch):
1538
 
        """Set the target reference of the branch in a_bzrdir.
 
1653
    def set_reference(self, controldir, name, to_branch):
 
1654
        """Set the target reference of the branch in controldir.
1539
1655
 
1540
1656
        format probing must have been completed before calling
1541
1657
        this method - it is assumed that the format of the branch
1542
 
        in a_bzrdir is correct.
 
1658
        in controldir is correct.
1543
1659
 
1544
 
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1660
        :param controldir: The controldir to set the branch reference for.
 
1661
        :param name: Name of colocated branch to set, None for default
1545
1662
        :param to_branch: branch that the checkout is to reference
1546
1663
        """
1547
1664
        raise NotImplementedError(self.set_reference)
1554
1671
        """Return the short format description for this format."""
1555
1672
        raise NotImplementedError(self.get_format_description)
1556
1673
 
1557
 
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
 
1674
    def _run_post_branch_init_hooks(self, controldir, name, branch):
1558
1675
        hooks = Branch.hooks['post_branch_init']
1559
1676
        if not hooks:
1560
1677
            return
1561
 
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
 
1678
        params = BranchInitHookParams(self, controldir, name, branch)
1562
1679
        for hook in hooks:
1563
1680
            hook(params)
1564
1681
 
1565
 
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1566
 
                           lock_type='metadir', set_format=True):
1567
 
        """Initialize a branch in a bzrdir, with specified files
1568
 
 
1569
 
        :param a_bzrdir: The bzrdir to initialize the branch in
1570
 
        :param utf8_files: The files to create as a list of
1571
 
            (filename, content) tuples
1572
 
        :param name: Name of colocated branch to create, if any
1573
 
        :param set_format: If True, set the format with
1574
 
            self.get_format_string.  (BzrBranch4 has its format set
1575
 
            elsewhere)
1576
 
        :return: a branch in this format
1577
 
        """
1578
 
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1579
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1580
 
        lock_map = {
1581
 
            'metadir': ('lock', lockdir.LockDir),
1582
 
            'branch4': ('branch-lock', lockable_files.TransportLock),
1583
 
        }
1584
 
        lock_name, lock_class = lock_map[lock_type]
1585
 
        control_files = lockable_files.LockableFiles(branch_transport,
1586
 
            lock_name, lock_class)
1587
 
        control_files.create_lock()
1588
 
        try:
1589
 
            control_files.lock_write()
1590
 
        except errors.LockContention:
1591
 
            if lock_type != 'branch4':
1592
 
                raise
1593
 
            lock_taken = False
1594
 
        else:
1595
 
            lock_taken = True
1596
 
        if set_format:
1597
 
            utf8_files += [('format', self.get_format_string())]
1598
 
        try:
1599
 
            for (filename, content) in utf8_files:
1600
 
                branch_transport.put_bytes(
1601
 
                    filename, content,
1602
 
                    mode=a_bzrdir._get_file_mode())
1603
 
        finally:
1604
 
            if lock_taken:
1605
 
                control_files.unlock()
1606
 
        branch = self.open(a_bzrdir, name, _found=True)
1607
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1608
 
        return branch
1609
 
 
1610
 
    def initialize(self, a_bzrdir, name=None):
1611
 
        """Create a branch of this format in a_bzrdir.
1612
 
        
 
1682
    def initialize(self, controldir, name=None, repository=None,
 
1683
                   append_revisions_only=None):
 
1684
        """Create a branch of this format in controldir.
 
1685
 
1613
1686
        :param name: Name of the colocated branch to create.
1614
1687
        """
1615
1688
        raise NotImplementedError(self.initialize)
1635
1708
        Note that it is normal for branch to be a RemoteBranch when using tags
1636
1709
        on a RemoteBranch.
1637
1710
        """
1638
 
        return DisabledTags(branch)
 
1711
        return _mod_tag.DisabledTags(branch)
1639
1712
 
1640
1713
    def network_name(self):
1641
1714
        """A simple byte string uniquely identifying this format for RPC calls.
1647
1720
        """
1648
1721
        raise NotImplementedError(self.network_name)
1649
1722
 
1650
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1651
 
        """Return the branch object for a_bzrdir
 
1723
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
 
1724
            found_repository=None, possible_transports=None):
 
1725
        """Return the branch object for controldir.
1652
1726
 
1653
 
        :param a_bzrdir: A BzrDir that contains a branch.
 
1727
        :param controldir: A ControlDir that contains a branch.
1654
1728
        :param name: Name of colocated branch to open
1655
1729
        :param _found: a private parameter, do not use it. It is used to
1656
1730
            indicate if format probing has already be done.
1660
1734
        raise NotImplementedError(self.open)
1661
1735
 
1662
1736
    @classmethod
 
1737
    @deprecated_method(deprecated_in((2, 4, 0)))
1663
1738
    def register_format(klass, format):
1664
 
        """Register a metadir format."""
1665
 
        klass._formats[format.get_format_string()] = format
1666
 
        # Metadir formats have a network name of their format string, and get
1667
 
        # registered as class factories.
1668
 
        network_format_registry.register(format.get_format_string(), format.__class__)
 
1739
        """Register a metadir format.
 
1740
 
 
1741
        See MetaDirBranchFormatFactory for the ability to register a format
 
1742
        without loading the code the format needs until it is actually used.
 
1743
        """
 
1744
        format_registry.register(format)
1669
1745
 
1670
1746
    @classmethod
 
1747
    @deprecated_method(deprecated_in((2, 4, 0)))
1671
1748
    def set_default_format(klass, format):
1672
 
        klass._default_format = format
 
1749
        format_registry.set_default(format)
1673
1750
 
1674
1751
    def supports_set_append_revisions_only(self):
1675
1752
        """True if this format supports set_append_revisions_only."""
1679
1756
        """True if this format records a stacked-on branch."""
1680
1757
        return False
1681
1758
 
 
1759
    def supports_leaving_lock(self):
 
1760
        """True if this format supports leaving locks in place."""
 
1761
        return False # by default
 
1762
 
1682
1763
    @classmethod
 
1764
    @deprecated_method(deprecated_in((2, 4, 0)))
1683
1765
    def unregister_format(klass, format):
1684
 
        del klass._formats[format.get_format_string()]
 
1766
        format_registry.remove(format)
1685
1767
 
1686
1768
    def __str__(self):
1687
1769
        return self.get_format_description().rstrip()
1690
1772
        """True if this format supports tags stored in the branch"""
1691
1773
        return False  # by default
1692
1774
 
 
1775
    def tags_are_versioned(self):
 
1776
        """Whether the tag container for this branch versions tags."""
 
1777
        return False
 
1778
 
 
1779
    def supports_tags_referencing_ghosts(self):
 
1780
        """True if tags can reference ghost revisions."""
 
1781
        return True
 
1782
 
 
1783
 
 
1784
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
 
1785
    """A factory for a BranchFormat object, permitting simple lazy registration.
 
1786
    
 
1787
    While none of the built in BranchFormats are lazy registered yet,
 
1788
    bzrlib.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
 
1789
    use it, and the bzr-loom plugin uses it as well (see
 
1790
    bzrlib.plugins.loom.formats).
 
1791
    """
 
1792
 
 
1793
    def __init__(self, format_string, module_name, member_name):
 
1794
        """Create a MetaDirBranchFormatFactory.
 
1795
 
 
1796
        :param format_string: The format string the format has.
 
1797
        :param module_name: Module to load the format class from.
 
1798
        :param member_name: Attribute name within the module for the format class.
 
1799
        """
 
1800
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
 
1801
        self._format_string = format_string
 
1802
        
 
1803
    def get_format_string(self):
 
1804
        """See BranchFormat.get_format_string."""
 
1805
        return self._format_string
 
1806
 
 
1807
    def __call__(self):
 
1808
        """Used for network_format_registry support."""
 
1809
        return self.get_obj()()
 
1810
 
1693
1811
 
1694
1812
class BranchHooks(Hooks):
1695
1813
    """A dictionary mapping hook name to a list of callables for branch hooks.
1704
1822
        These are all empty initially, because by default nothing should get
1705
1823
        notified.
1706
1824
        """
1707
 
        Hooks.__init__(self)
1708
 
        self.create_hook(HookPoint('set_rh',
 
1825
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
 
1826
        self.add_hook('set_rh',
1709
1827
            "Invoked whenever the revision history has been set via "
1710
1828
            "set_revision_history. The api signature is (branch, "
1711
1829
            "revision_history), and the branch will be write-locked. "
1712
1830
            "The set_rh hook can be expensive for bzr to trigger, a better "
1713
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1714
 
        self.create_hook(HookPoint('open',
 
1831
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
 
1832
        self.add_hook('open',
1715
1833
            "Called with the Branch object that has been opened after a "
1716
 
            "branch is opened.", (1, 8), None))
1717
 
        self.create_hook(HookPoint('post_push',
 
1834
            "branch is opened.", (1, 8))
 
1835
        self.add_hook('post_push',
1718
1836
            "Called after a push operation completes. post_push is called "
1719
1837
            "with a bzrlib.branch.BranchPushResult object and only runs in the "
1720
 
            "bzr client.", (0, 15), None))
1721
 
        self.create_hook(HookPoint('post_pull',
 
1838
            "bzr client.", (0, 15))
 
1839
        self.add_hook('post_pull',
1722
1840
            "Called after a pull operation completes. post_pull is called "
1723
1841
            "with a bzrlib.branch.PullResult object and only runs in the "
1724
 
            "bzr client.", (0, 15), None))
1725
 
        self.create_hook(HookPoint('pre_commit',
1726
 
            "Called after a commit is calculated but before it is is "
 
1842
            "bzr client.", (0, 15))
 
1843
        self.add_hook('pre_commit',
 
1844
            "Called after a commit is calculated but before it is "
1727
1845
            "completed. pre_commit is called with (local, master, old_revno, "
1728
1846
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1729
1847
            "). old_revid is NULL_REVISION for the first commit to a branch, "
1731
1849
            "basis revision. hooks MUST NOT modify this delta. "
1732
1850
            " future_tree is an in-memory tree obtained from "
1733
1851
            "CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1734
 
            "tree.", (0,91), None))
1735
 
        self.create_hook(HookPoint('post_commit',
 
1852
            "tree.", (0,91))
 
1853
        self.add_hook('post_commit',
1736
1854
            "Called in the bzr client after a commit has completed. "
1737
1855
            "post_commit is called with (local, master, old_revno, old_revid, "
1738
1856
            "new_revno, new_revid). old_revid is NULL_REVISION for the first "
1739
 
            "commit to a branch.", (0, 15), None))
1740
 
        self.create_hook(HookPoint('post_uncommit',
 
1857
            "commit to a branch.", (0, 15))
 
1858
        self.add_hook('post_uncommit',
1741
1859
            "Called in the bzr client after an uncommit completes. "
1742
1860
            "post_uncommit is called with (local, master, old_revno, "
1743
1861
            "old_revid, new_revno, new_revid) where local is the local branch "
1744
1862
            "or None, master is the target branch, and an empty branch "
1745
 
            "receives new_revno of 0, new_revid of None.", (0, 15), None))
1746
 
        self.create_hook(HookPoint('pre_change_branch_tip',
 
1863
            "receives new_revno of 0, new_revid of None.", (0, 15))
 
1864
        self.add_hook('pre_change_branch_tip',
1747
1865
            "Called in bzr client and server before a change to the tip of a "
1748
1866
            "branch is made. pre_change_branch_tip is called with a "
1749
1867
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1750
 
            "commit, uncommit will all trigger this hook.", (1, 6), None))
1751
 
        self.create_hook(HookPoint('post_change_branch_tip',
 
1868
            "commit, uncommit will all trigger this hook.", (1, 6))
 
1869
        self.add_hook('post_change_branch_tip',
1752
1870
            "Called in bzr client and server after a change to the tip of a "
1753
1871
            "branch is made. post_change_branch_tip is called with a "
1754
1872
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1755
 
            "commit, uncommit will all trigger this hook.", (1, 4), None))
1756
 
        self.create_hook(HookPoint('transform_fallback_location',
 
1873
            "commit, uncommit will all trigger this hook.", (1, 4))
 
1874
        self.add_hook('transform_fallback_location',
1757
1875
            "Called when a stacked branch is activating its fallback "
1758
1876
            "locations. transform_fallback_location is called with (branch, "
1759
1877
            "url), and should return a new url. Returning the same url "
1764
1882
            "fallback locations have not been activated. When there are "
1765
1883
            "multiple hooks installed for transform_fallback_location, "
1766
1884
            "all are called with the url returned from the previous hook."
1767
 
            "The order is however undefined.", (1, 9), None))
1768
 
        self.create_hook(HookPoint('automatic_tag_name',
1769
 
            "Called to determine an automatic tag name for a revision."
 
1885
            "The order is however undefined.", (1, 9))
 
1886
        self.add_hook('automatic_tag_name',
 
1887
            "Called to determine an automatic tag name for a revision. "
1770
1888
            "automatic_tag_name is called with (branch, revision_id) and "
1771
1889
            "should return a tag name or None if no tag name could be "
1772
1890
            "determined. The first non-None tag name returned will be used.",
1773
 
            (2, 2), None))
1774
 
        self.create_hook(HookPoint('post_branch_init',
 
1891
            (2, 2))
 
1892
        self.add_hook('post_branch_init',
1775
1893
            "Called after new branch initialization completes. "
1776
1894
            "post_branch_init is called with a "
1777
1895
            "bzrlib.branch.BranchInitHookParams. "
1778
1896
            "Note that init, branch and checkout (both heavyweight and "
1779
 
            "lightweight) will all trigger this hook.", (2, 2), None))
1780
 
        self.create_hook(HookPoint('post_switch',
 
1897
            "lightweight) will all trigger this hook.", (2, 2))
 
1898
        self.add_hook('post_switch',
1781
1899
            "Called after a checkout switches branch. "
1782
1900
            "post_switch is called with a "
1783
 
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
 
1901
            "bzrlib.branch.SwitchHookParams.", (2, 2))
1784
1902
 
1785
1903
 
1786
1904
 
1789
1907
 
1790
1908
 
1791
1909
class ChangeBranchTipParams(object):
1792
 
    """Object holding parameters passed to *_change_branch_tip hooks.
 
1910
    """Object holding parameters passed to `*_change_branch_tip` hooks.
1793
1911
 
1794
1912
    There are 5 fields that hooks may wish to access:
1795
1913
 
1827
1945
 
1828
1946
 
1829
1947
class BranchInitHookParams(object):
1830
 
    """Object holding parameters passed to *_branch_init hooks.
 
1948
    """Object holding parameters passed to `*_branch_init` hooks.
1831
1949
 
1832
1950
    There are 4 fields that hooks may wish to access:
1833
1951
 
1834
1952
    :ivar format: the branch format
1835
 
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
 
1953
    :ivar bzrdir: the ControlDir where the branch will be/has been initialized
1836
1954
    :ivar name: name of colocated branch, if any (or None)
1837
1955
    :ivar branch: the branch created
1838
1956
 
1841
1959
    branch, which refer to the original branch.
1842
1960
    """
1843
1961
 
1844
 
    def __init__(self, format, a_bzrdir, name, branch):
 
1962
    def __init__(self, format, controldir, name, branch):
1845
1963
        """Create a group of BranchInitHook parameters.
1846
1964
 
1847
1965
        :param format: the branch format
1848
 
        :param a_bzrdir: the BzrDir where the branch will be/has been
 
1966
        :param controldir: the ControlDir where the branch will be/has been
1849
1967
            initialized
1850
1968
        :param name: name of colocated branch, if any (or None)
1851
1969
        :param branch: the branch created
1855
1973
        in branch, which refer to the original branch.
1856
1974
        """
1857
1975
        self.format = format
1858
 
        self.bzrdir = a_bzrdir
 
1976
        self.bzrdir = controldir
1859
1977
        self.name = name
1860
1978
        self.branch = branch
1861
1979
 
1863
1981
        return self.__dict__ == other.__dict__
1864
1982
 
1865
1983
    def __repr__(self):
1866
 
        if self.branch:
1867
 
            return "<%s of %s>" % (self.__class__.__name__, self.branch)
1868
 
        else:
1869
 
            return "<%s of format:%s bzrdir:%s>" % (
1870
 
                self.__class__.__name__, self.branch,
1871
 
                self.format, self.bzrdir)
 
1984
        return "<%s of %s>" % (self.__class__.__name__, self.branch)
1872
1985
 
1873
1986
 
1874
1987
class SwitchHookParams(object):
1875
 
    """Object holding parameters passed to *_switch hooks.
 
1988
    """Object holding parameters passed to `*_switch` hooks.
1876
1989
 
1877
1990
    There are 4 fields that hooks may wish to access:
1878
1991
 
1879
 
    :ivar control_dir: BzrDir of the checkout to change
 
1992
    :ivar control_dir: ControlDir of the checkout to change
1880
1993
    :ivar to_branch: branch that the checkout is to reference
1881
1994
    :ivar force: skip the check for local commits in a heavy checkout
1882
1995
    :ivar revision_id: revision ID to switch to (or None)
1885
1998
    def __init__(self, control_dir, to_branch, force, revision_id):
1886
1999
        """Create a group of SwitchHook parameters.
1887
2000
 
1888
 
        :param control_dir: BzrDir of the checkout to change
 
2001
        :param control_dir: ControlDir of the checkout to change
1889
2002
        :param to_branch: branch that the checkout is to reference
1890
2003
        :param force: skip the check for local commits in a heavy checkout
1891
2004
        :param revision_id: revision ID to switch to (or None)
1904
2017
            self.revision_id)
1905
2018
 
1906
2019
 
1907
 
class BzrBranchFormat4(BranchFormat):
1908
 
    """Bzr branch format 4.
1909
 
 
1910
 
    This format has:
1911
 
     - a revision-history file.
1912
 
     - a branch-lock lock file [ to be shared with the bzrdir ]
1913
 
    """
1914
 
 
1915
 
    def get_format_description(self):
1916
 
        """See BranchFormat.get_format_description()."""
1917
 
        return "Branch format 4"
1918
 
 
1919
 
    def initialize(self, a_bzrdir, name=None):
1920
 
        """Create a branch of this format in a_bzrdir."""
1921
 
        utf8_files = [('revision-history', ''),
1922
 
                      ('branch-name', ''),
1923
 
                      ]
1924
 
        return self._initialize_helper(a_bzrdir, utf8_files, name=name,
1925
 
                                       lock_type='branch4', set_format=False)
1926
 
 
1927
 
    def __init__(self):
1928
 
        super(BzrBranchFormat4, self).__init__()
1929
 
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
1930
 
 
1931
 
    def network_name(self):
1932
 
        """The network name for this format is the control dirs disk label."""
1933
 
        return self._matchingbzrdir.get_format_string()
1934
 
 
1935
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1936
 
        """See BranchFormat.open()."""
1937
 
        if not _found:
1938
 
            # we are being called directly and must probe.
1939
 
            raise NotImplementedError
1940
 
        return BzrBranch(_format=self,
1941
 
                         _control_files=a_bzrdir._control_files,
1942
 
                         a_bzrdir=a_bzrdir,
1943
 
                         name=name,
1944
 
                         _repository=a_bzrdir.open_repository())
1945
 
 
1946
 
    def __str__(self):
1947
 
        return "Bazaar-NG branch format 4"
1948
 
 
1949
 
 
1950
2020
class BranchFormatMetadir(BranchFormat):
1951
2021
    """Common logic for meta-dir based branch formats."""
1952
2022
 
1954
2024
        """What class to instantiate on open calls."""
1955
2025
        raise NotImplementedError(self._branch_class)
1956
2026
 
 
2027
    def _get_initial_config(self, append_revisions_only=None):
 
2028
        if append_revisions_only:
 
2029
            return "append_revisions_only = True\n"
 
2030
        else:
 
2031
            # Avoid writing anything if append_revisions_only is disabled,
 
2032
            # as that is the default.
 
2033
            return ""
 
2034
 
 
2035
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
2036
                           repository=None):
 
2037
        """Initialize a branch in a bzrdir, with specified files
 
2038
 
 
2039
        :param a_bzrdir: The bzrdir to initialize the branch in
 
2040
        :param utf8_files: The files to create as a list of
 
2041
            (filename, content) tuples
 
2042
        :param name: Name of colocated branch to create, if any
 
2043
        :return: a branch in this format
 
2044
        """
 
2045
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
2046
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
 
2047
        control_files = lockable_files.LockableFiles(branch_transport,
 
2048
            'lock', lockdir.LockDir)
 
2049
        control_files.create_lock()
 
2050
        control_files.lock_write()
 
2051
        try:
 
2052
            utf8_files += [('format', self.get_format_string())]
 
2053
            for (filename, content) in utf8_files:
 
2054
                branch_transport.put_bytes(
 
2055
                    filename, content,
 
2056
                    mode=a_bzrdir._get_file_mode())
 
2057
        finally:
 
2058
            control_files.unlock()
 
2059
        branch = self.open(a_bzrdir, name, _found=True,
 
2060
                found_repository=repository)
 
2061
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
2062
        return branch
 
2063
 
1957
2064
    def network_name(self):
1958
2065
        """A simple byte string uniquely identifying this format for RPC calls.
1959
2066
 
1961
2068
        """
1962
2069
        return self.get_format_string()
1963
2070
 
1964
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
2071
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
 
2072
            found_repository=None, possible_transports=None):
1965
2073
        """See BranchFormat.open()."""
1966
2074
        if not _found:
1967
2075
            format = BranchFormat.find_format(a_bzrdir, name=name)
1972
2080
        try:
1973
2081
            control_files = lockable_files.LockableFiles(transport, 'lock',
1974
2082
                                                         lockdir.LockDir)
 
2083
            if found_repository is None:
 
2084
                found_repository = a_bzrdir.find_repository()
1975
2085
            return self._branch_class()(_format=self,
1976
2086
                              _control_files=control_files,
1977
2087
                              name=name,
1978
2088
                              a_bzrdir=a_bzrdir,
1979
 
                              _repository=a_bzrdir.find_repository(),
1980
 
                              ignore_fallbacks=ignore_fallbacks)
 
2089
                              _repository=found_repository,
 
2090
                              ignore_fallbacks=ignore_fallbacks,
 
2091
                              possible_transports=possible_transports)
1981
2092
        except errors.NoSuchFile:
1982
2093
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1983
2094
 
1984
 
    def __init__(self):
1985
 
        super(BranchFormatMetadir, self).__init__()
1986
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
1987
 
        self._matchingbzrdir.set_branch_format(self)
 
2095
    @property
 
2096
    def _matchingbzrdir(self):
 
2097
        ret = bzrdir.BzrDirMetaFormat1()
 
2098
        ret.set_branch_format(self)
 
2099
        return ret
1988
2100
 
1989
2101
    def supports_tags(self):
1990
2102
        return True
1991
2103
 
 
2104
    def supports_leaving_lock(self):
 
2105
        return True
 
2106
 
1992
2107
 
1993
2108
class BzrBranchFormat5(BranchFormatMetadir):
1994
2109
    """Bzr branch format 5.
2014
2129
        """See BranchFormat.get_format_description()."""
2015
2130
        return "Branch format 5"
2016
2131
 
2017
 
    def initialize(self, a_bzrdir, name=None):
 
2132
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2133
                   append_revisions_only=None):
2018
2134
        """Create a branch of this format in a_bzrdir."""
 
2135
        if append_revisions_only:
 
2136
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2019
2137
        utf8_files = [('revision-history', ''),
2020
2138
                      ('branch-name', ''),
2021
2139
                      ]
2022
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2140
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2023
2141
 
2024
2142
    def supports_tags(self):
2025
2143
        return False
2047
2165
        """See BranchFormat.get_format_description()."""
2048
2166
        return "Branch format 6"
2049
2167
 
2050
 
    def initialize(self, a_bzrdir, name=None):
 
2168
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2169
                   append_revisions_only=None):
2051
2170
        """Create a branch of this format in a_bzrdir."""
2052
2171
        utf8_files = [('last-revision', '0 null:\n'),
2053
 
                      ('branch.conf', ''),
 
2172
                      ('branch.conf',
 
2173
                          self._get_initial_config(append_revisions_only)),
2054
2174
                      ('tags', ''),
2055
2175
                      ]
2056
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2176
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2057
2177
 
2058
2178
    def make_tags(self, branch):
2059
2179
        """See bzrlib.branch.BranchFormat.make_tags()."""
2060
 
        return BasicTags(branch)
 
2180
        return _mod_tag.BasicTags(branch)
2061
2181
 
2062
2182
    def supports_set_append_revisions_only(self):
2063
2183
        return True
2077
2197
        """See BranchFormat.get_format_description()."""
2078
2198
        return "Branch format 8"
2079
2199
 
2080
 
    def initialize(self, a_bzrdir, name=None):
 
2200
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2201
                   append_revisions_only=None):
2081
2202
        """Create a branch of this format in a_bzrdir."""
2082
2203
        utf8_files = [('last-revision', '0 null:\n'),
2083
 
                      ('branch.conf', ''),
 
2204
                      ('branch.conf',
 
2205
                          self._get_initial_config(append_revisions_only)),
2084
2206
                      ('tags', ''),
2085
2207
                      ('references', '')
2086
2208
                      ]
2087
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2088
 
 
2089
 
    def __init__(self):
2090
 
        super(BzrBranchFormat8, self).__init__()
2091
 
        self._matchingbzrdir.repository_format = \
2092
 
            RepositoryFormatKnitPack5RichRoot()
 
2209
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2093
2210
 
2094
2211
    def make_tags(self, branch):
2095
2212
        """See bzrlib.branch.BranchFormat.make_tags()."""
2096
 
        return BasicTags(branch)
 
2213
        return _mod_tag.BasicTags(branch)
2097
2214
 
2098
2215
    def supports_set_append_revisions_only(self):
2099
2216
        return True
2104
2221
    supports_reference_locations = True
2105
2222
 
2106
2223
 
2107
 
class BzrBranchFormat7(BzrBranchFormat8):
 
2224
class BzrBranchFormat7(BranchFormatMetadir):
2108
2225
    """Branch format with last-revision, tags, and a stacked location pointer.
2109
2226
 
2110
2227
    The stacked location pointer is passed down to the repository and requires
2113
2230
    This format was introduced in bzr 1.6.
2114
2231
    """
2115
2232
 
2116
 
    def initialize(self, a_bzrdir, name=None):
 
2233
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2234
                   append_revisions_only=None):
2117
2235
        """Create a branch of this format in a_bzrdir."""
2118
2236
        utf8_files = [('last-revision', '0 null:\n'),
2119
 
                      ('branch.conf', ''),
 
2237
                      ('branch.conf',
 
2238
                          self._get_initial_config(append_revisions_only)),
2120
2239
                      ('tags', ''),
2121
2240
                      ]
2122
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2241
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2123
2242
 
2124
2243
    def _branch_class(self):
2125
2244
        return BzrBranch7
2135
2254
    def supports_set_append_revisions_only(self):
2136
2255
        return True
2137
2256
 
 
2257
    def supports_stacking(self):
 
2258
        return True
 
2259
 
 
2260
    def make_tags(self, branch):
 
2261
        """See bzrlib.branch.BranchFormat.make_tags()."""
 
2262
        return _mod_tag.BasicTags(branch)
 
2263
 
2138
2264
    supports_reference_locations = False
2139
2265
 
2140
2266
 
2141
 
class BranchReferenceFormat(BranchFormat):
 
2267
class BranchReferenceFormat(BranchFormatMetadir):
2142
2268
    """Bzr branch reference format.
2143
2269
 
2144
2270
    Branch references are used in implementing checkouts, they
2157
2283
        """See BranchFormat.get_format_description()."""
2158
2284
        return "Checkout reference format 1"
2159
2285
 
2160
 
    def get_reference(self, a_bzrdir):
 
2286
    def get_reference(self, a_bzrdir, name=None):
2161
2287
        """See BranchFormat.get_reference()."""
2162
 
        transport = a_bzrdir.get_branch_transport(None)
 
2288
        transport = a_bzrdir.get_branch_transport(None, name=name)
2163
2289
        return transport.get_bytes('location')
2164
2290
 
2165
 
    def set_reference(self, a_bzrdir, to_branch):
 
2291
    def set_reference(self, a_bzrdir, name, to_branch):
2166
2292
        """See BranchFormat.set_reference()."""
2167
 
        transport = a_bzrdir.get_branch_transport(None)
 
2293
        transport = a_bzrdir.get_branch_transport(None, name=name)
2168
2294
        location = transport.put_bytes('location', to_branch.base)
2169
2295
 
2170
 
    def initialize(self, a_bzrdir, name=None, target_branch=None):
 
2296
    def initialize(self, a_bzrdir, name=None, target_branch=None,
 
2297
            repository=None, append_revisions_only=None):
2171
2298
        """Create a branch of this format in a_bzrdir."""
2172
2299
        if target_branch is None:
2173
2300
            # this format does not implement branch itself, thus the implicit
2174
2301
            # creation contract must see it as uninitializable
2175
2302
            raise errors.UninitializableFormat(self)
2176
2303
        mutter('creating branch reference in %s', a_bzrdir.user_url)
 
2304
        if a_bzrdir._format.fixed_components:
 
2305
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
2177
2306
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2178
2307
        branch_transport.put_bytes('location',
2179
 
            target_branch.bzrdir.user_url)
 
2308
            target_branch.user_url)
2180
2309
        branch_transport.put_bytes('format', self.get_format_string())
2181
2310
        branch = self.open(
2182
2311
            a_bzrdir, name, _found=True,
2184
2313
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2185
2314
        return branch
2186
2315
 
2187
 
    def __init__(self):
2188
 
        super(BranchReferenceFormat, self).__init__()
2189
 
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
2190
 
        self._matchingbzrdir.set_branch_format(self)
2191
 
 
2192
2316
    def _make_reference_clone_function(format, a_branch):
2193
2317
        """Create a clone() routine for a branch dynamically."""
2194
2318
        def clone(to_bzrdir, revision_id=None,
2201
2325
        return clone
2202
2326
 
2203
2327
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2204
 
             possible_transports=None, ignore_fallbacks=False):
 
2328
             possible_transports=None, ignore_fallbacks=False,
 
2329
             found_repository=None):
2205
2330
        """Return the branch that the branch reference in a_bzrdir points at.
2206
2331
 
2207
2332
        :param a_bzrdir: A BzrDir that contains a branch.
2221
2346
                raise AssertionError("wrong format %r found for %r" %
2222
2347
                    (format, self))
2223
2348
        if location is None:
2224
 
            location = self.get_reference(a_bzrdir)
2225
 
        real_bzrdir = bzrdir.BzrDir.open(
 
2349
            location = self.get_reference(a_bzrdir, name)
 
2350
        real_bzrdir = controldir.ControlDir.open(
2226
2351
            location, possible_transports=possible_transports)
2227
2352
        result = real_bzrdir.open_branch(name=name, 
2228
 
            ignore_fallbacks=ignore_fallbacks)
 
2353
            ignore_fallbacks=ignore_fallbacks,
 
2354
            possible_transports=possible_transports)
2229
2355
        # this changes the behaviour of result.clone to create a new reference
2230
2356
        # rather than a copy of the content of the branch.
2231
2357
        # I did not use a proxy object because that needs much more extensive
2238
2364
        return result
2239
2365
 
2240
2366
 
 
2367
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
 
2368
    """Branch format registry."""
 
2369
 
 
2370
    def __init__(self, other_registry=None):
 
2371
        super(BranchFormatRegistry, self).__init__(other_registry)
 
2372
        self._default_format = None
 
2373
 
 
2374
    def set_default(self, format):
 
2375
        self._default_format = format
 
2376
 
 
2377
    def get_default(self):
 
2378
        return self._default_format
 
2379
 
 
2380
 
2241
2381
network_format_registry = registry.FormatRegistry()
2242
2382
"""Registry of formats indexed by their network name.
2243
2383
 
2246
2386
BranchFormat.network_name() for more detail.
2247
2387
"""
2248
2388
 
 
2389
format_registry = BranchFormatRegistry(network_format_registry)
 
2390
 
2249
2391
 
2250
2392
# formats which have no format string are not discoverable
2251
2393
# and not independently creatable, so are not registered.
2253
2395
__format6 = BzrBranchFormat6()
2254
2396
__format7 = BzrBranchFormat7()
2255
2397
__format8 = BzrBranchFormat8()
2256
 
BranchFormat.register_format(__format5)
2257
 
BranchFormat.register_format(BranchReferenceFormat())
2258
 
BranchFormat.register_format(__format6)
2259
 
BranchFormat.register_format(__format7)
2260
 
BranchFormat.register_format(__format8)
2261
 
BranchFormat.set_default_format(__format7)
2262
 
_legacy_formats = [BzrBranchFormat4(),
2263
 
    ]
2264
 
network_format_registry.register(
2265
 
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
 
2398
format_registry.register(__format5)
 
2399
format_registry.register(BranchReferenceFormat())
 
2400
format_registry.register(__format6)
 
2401
format_registry.register(__format7)
 
2402
format_registry.register(__format8)
 
2403
format_registry.set_default(__format7)
 
2404
 
 
2405
 
 
2406
class BranchWriteLockResult(LogicalLockResult):
 
2407
    """The result of write locking a branch.
 
2408
 
 
2409
    :ivar branch_token: The token obtained from the underlying branch lock, or
 
2410
        None.
 
2411
    :ivar unlock: A callable which will unlock the lock.
 
2412
    """
 
2413
 
 
2414
    def __init__(self, unlock, branch_token):
 
2415
        LogicalLockResult.__init__(self, unlock)
 
2416
        self.branch_token = branch_token
 
2417
 
 
2418
    def __repr__(self):
 
2419
        return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
 
2420
            self.unlock)
2266
2421
 
2267
2422
 
2268
2423
class BzrBranch(Branch, _RelockDebugMixin):
2283
2438
 
2284
2439
    def __init__(self, _format=None,
2285
2440
                 _control_files=None, a_bzrdir=None, name=None,
2286
 
                 _repository=None, ignore_fallbacks=False):
 
2441
                 _repository=None, ignore_fallbacks=False,
 
2442
                 possible_transports=None):
2287
2443
        """Create new branch object at a particular location."""
2288
2444
        if a_bzrdir is None:
2289
2445
            raise ValueError('a_bzrdir must be supplied')
2290
2446
        else:
2291
2447
            self.bzrdir = a_bzrdir
2292
 
        self._base = self.bzrdir.transport.clone('..').base
 
2448
        self._user_transport = self.bzrdir.transport.clone('..')
 
2449
        if name is not None:
 
2450
            self._user_transport.set_segment_parameter(
 
2451
                "branch", urlutils.escape(name))
 
2452
        self._base = self._user_transport.base
2293
2453
        self.name = name
2294
 
        # XXX: We should be able to just do
2295
 
        #   self.base = self.bzrdir.root_transport.base
2296
 
        # but this does not quite work yet -- mbp 20080522
2297
2454
        self._format = _format
2298
2455
        if _control_files is None:
2299
2456
            raise ValueError('BzrBranch _control_files is None')
2300
2457
        self.control_files = _control_files
2301
2458
        self._transport = _control_files._transport
2302
2459
        self.repository = _repository
2303
 
        Branch.__init__(self)
 
2460
        Branch.__init__(self, possible_transports)
2304
2461
 
2305
2462
    def __str__(self):
2306
 
        if self.name is None:
2307
 
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
2308
 
        else:
2309
 
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
2310
 
                self.name)
 
2463
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
2311
2464
 
2312
2465
    __repr__ = __str__
2313
2466
 
2317
2470
 
2318
2471
    base = property(_get_base, doc="The URL for the root of this branch.")
2319
2472
 
 
2473
    @property
 
2474
    def user_transport(self):
 
2475
        return self._user_transport
 
2476
 
2320
2477
    def _get_config(self):
2321
 
        return TransportConfig(self._transport, 'branch.conf')
 
2478
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
 
2479
 
 
2480
    def _get_config_store(self):
 
2481
        return _mod_config.BranchStore(self)
2322
2482
 
2323
2483
    def is_locked(self):
2324
2484
        return self.control_files.is_locked()
2325
2485
 
2326
2486
    def lock_write(self, token=None):
 
2487
        """Lock the branch for write operations.
 
2488
 
 
2489
        :param token: A token to permit reacquiring a previously held and
 
2490
            preserved lock.
 
2491
        :return: A BranchWriteLockResult.
 
2492
        """
2327
2493
        if not self.is_locked():
2328
2494
            self._note_lock('w')
2329
2495
        # All-in-one needs to always unlock/lock.
2335
2501
        else:
2336
2502
            took_lock = False
2337
2503
        try:
2338
 
            return self.control_files.lock_write(token=token)
 
2504
            return BranchWriteLockResult(self.unlock,
 
2505
                self.control_files.lock_write(token=token))
2339
2506
        except:
2340
2507
            if took_lock:
2341
2508
                self.repository.unlock()
2342
2509
            raise
2343
2510
 
2344
2511
    def lock_read(self):
 
2512
        """Lock the branch for read operations.
 
2513
 
 
2514
        :return: A bzrlib.lock.LogicalLockResult.
 
2515
        """
2345
2516
        if not self.is_locked():
2346
2517
            self._note_lock('r')
2347
2518
        # All-in-one needs to always unlock/lock.
2354
2525
            took_lock = False
2355
2526
        try:
2356
2527
            self.control_files.lock_read()
 
2528
            return LogicalLockResult(self.unlock)
2357
2529
        except:
2358
2530
            if took_lock:
2359
2531
                self.repository.unlock()
2387
2559
        """See Branch.print_file."""
2388
2560
        return self.repository.print_file(file, revision_id)
2389
2561
 
2390
 
    def _write_revision_history(self, history):
2391
 
        """Factored out of set_revision_history.
2392
 
 
2393
 
        This performs the actual writing to disk.
2394
 
        It is intended to be called by BzrBranch5.set_revision_history."""
2395
 
        self._transport.put_bytes(
2396
 
            'revision-history', '\n'.join(history),
2397
 
            mode=self.bzrdir._get_file_mode())
2398
 
 
2399
 
    @needs_write_lock
2400
 
    def set_revision_history(self, rev_history):
2401
 
        """See Branch.set_revision_history."""
2402
 
        if 'evil' in debug.debug_flags:
2403
 
            mutter_callsite(3, "set_revision_history scales with history.")
2404
 
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2405
 
        for rev_id in rev_history:
2406
 
            check_not_reserved_id(rev_id)
2407
 
        if Branch.hooks['post_change_branch_tip']:
2408
 
            # Don't calculate the last_revision_info() if there are no hooks
2409
 
            # that will use it.
2410
 
            old_revno, old_revid = self.last_revision_info()
2411
 
        if len(rev_history) == 0:
2412
 
            revid = _mod_revision.NULL_REVISION
2413
 
        else:
2414
 
            revid = rev_history[-1]
2415
 
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2416
 
        self._write_revision_history(rev_history)
2417
 
        self._clear_cached_state()
2418
 
        self._cache_revision_history(rev_history)
2419
 
        for hook in Branch.hooks['set_rh']:
2420
 
            hook(self, rev_history)
2421
 
        if Branch.hooks['post_change_branch_tip']:
2422
 
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2423
 
 
2424
 
    def _synchronize_history(self, destination, revision_id):
2425
 
        """Synchronize last revision and revision history between branches.
2426
 
 
2427
 
        This version is most efficient when the destination is also a
2428
 
        BzrBranch5, but works for BzrBranch6 as long as the revision
2429
 
        history is the true lefthand parent history, and all of the revisions
2430
 
        are in the destination's repository.  If not, set_revision_history
2431
 
        will fail.
2432
 
 
2433
 
        :param destination: The branch to copy the history into
2434
 
        :param revision_id: The revision-id to truncate history at.  May
2435
 
          be None to copy complete history.
2436
 
        """
2437
 
        if not isinstance(destination._format, BzrBranchFormat5):
2438
 
            super(BzrBranch, self)._synchronize_history(
2439
 
                destination, revision_id)
2440
 
            return
2441
 
        if revision_id == _mod_revision.NULL_REVISION:
2442
 
            new_history = []
2443
 
        else:
2444
 
            new_history = self.revision_history()
2445
 
        if revision_id is not None and new_history != []:
2446
 
            try:
2447
 
                new_history = new_history[:new_history.index(revision_id) + 1]
2448
 
            except ValueError:
2449
 
                rev = self.repository.get_revision(revision_id)
2450
 
                new_history = rev.get_history(self.repository)[1:]
2451
 
        destination.set_revision_history(new_history)
2452
 
 
2453
2562
    @needs_write_lock
2454
2563
    def set_last_revision_info(self, revno, revision_id):
2455
 
        """Set the last revision of this branch.
2456
 
 
2457
 
        The caller is responsible for checking that the revno is correct
2458
 
        for this revision id.
2459
 
 
2460
 
        It may be possible to set the branch last revision to an id not
2461
 
        present in the repository.  However, branches can also be
2462
 
        configured to check constraints on history, in which case this may not
2463
 
        be permitted.
2464
 
        """
 
2564
        if not revision_id or not isinstance(revision_id, basestring):
 
2565
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2465
2566
        revision_id = _mod_revision.ensure_null(revision_id)
2466
 
        # this old format stores the full history, but this api doesn't
2467
 
        # provide it, so we must generate, and might as well check it's
2468
 
        # correct
2469
 
        history = self._lefthand_history(revision_id)
2470
 
        if len(history) != revno:
2471
 
            raise AssertionError('%d != %d' % (len(history), revno))
2472
 
        self.set_revision_history(history)
2473
 
 
2474
 
    def _gen_revision_history(self):
2475
 
        history = self._transport.get_bytes('revision-history').split('\n')
2476
 
        if history[-1:] == ['']:
2477
 
            # There shouldn't be a trailing newline, but just in case.
2478
 
            history.pop()
2479
 
        return history
2480
 
 
2481
 
    @needs_write_lock
2482
 
    def generate_revision_history(self, revision_id, last_rev=None,
2483
 
        other_branch=None):
2484
 
        """Create a new revision history that will finish with revision_id.
2485
 
 
2486
 
        :param revision_id: the new tip to use.
2487
 
        :param last_rev: The previous last_revision. If not None, then this
2488
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
2489
 
        :param other_branch: The other branch that DivergedBranches should
2490
 
            raise with respect to.
2491
 
        """
2492
 
        self.set_revision_history(self._lefthand_history(revision_id,
2493
 
            last_rev, other_branch))
 
2567
        old_revno, old_revid = self.last_revision_info()
 
2568
        if self.get_append_revisions_only():
 
2569
            self._check_history_violation(revision_id)
 
2570
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
 
2571
        self._write_last_revision_info(revno, revision_id)
 
2572
        self._clear_cached_state()
 
2573
        self._last_revision_info_cache = revno, revision_id
 
2574
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2494
2575
 
2495
2576
    def basis_tree(self):
2496
2577
        """See Branch.basis_tree."""
2505
2586
                pass
2506
2587
        return None
2507
2588
 
2508
 
    def _basic_push(self, target, overwrite, stop_revision):
2509
 
        """Basic implementation of push without bound branches or hooks.
2510
 
 
2511
 
        Must be called with source read locked and target write locked.
2512
 
        """
2513
 
        result = BranchPushResult()
2514
 
        result.source_branch = self
2515
 
        result.target_branch = target
2516
 
        result.old_revno, result.old_revid = target.last_revision_info()
2517
 
        self.update_references(target)
2518
 
        if result.old_revid != self.last_revision():
2519
 
            # We assume that during 'push' this repository is closer than
2520
 
            # the target.
2521
 
            graph = self.repository.get_graph(target.repository)
2522
 
            target.update_revisions(self, stop_revision,
2523
 
                overwrite=overwrite, graph=graph)
2524
 
        if self._push_should_merge_tags():
2525
 
            result.tag_conflicts = self.tags.merge_to(target.tags,
2526
 
                overwrite)
2527
 
        result.new_revno, result.new_revid = target.last_revision_info()
2528
 
        return result
2529
 
 
2530
2589
    def get_stacked_on_url(self):
2531
2590
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2532
2591
 
2543
2602
            self._transport.put_bytes('parent', url + '\n',
2544
2603
                mode=self.bzrdir._get_file_mode())
2545
2604
 
2546
 
 
2547
 
class BzrBranch5(BzrBranch):
2548
 
    """A format 5 branch. This supports new features over plain branches.
2549
 
 
2550
 
    It has support for a master_branch which is the data for bound branches.
2551
 
    """
2552
 
 
2553
 
    def get_bound_location(self):
2554
 
        try:
2555
 
            return self._transport.get_bytes('bound')[:-1]
2556
 
        except errors.NoSuchFile:
2557
 
            return None
2558
 
 
2559
 
    @needs_read_lock
2560
 
    def get_master_branch(self, possible_transports=None):
2561
 
        """Return the branch we are bound to.
2562
 
 
2563
 
        :return: Either a Branch, or None
2564
 
 
2565
 
        This could memoise the branch, but if thats done
2566
 
        it must be revalidated on each new lock.
2567
 
        So for now we just don't memoise it.
2568
 
        # RBC 20060304 review this decision.
2569
 
        """
2570
 
        bound_loc = self.get_bound_location()
2571
 
        if not bound_loc:
2572
 
            return None
2573
 
        try:
2574
 
            return Branch.open(bound_loc,
2575
 
                               possible_transports=possible_transports)
2576
 
        except (errors.NotBranchError, errors.ConnectionError), e:
2577
 
            raise errors.BoundBranchConnectionFailure(
2578
 
                    self, bound_loc, e)
2579
 
 
2580
2605
    @needs_write_lock
2581
 
    def set_bound_location(self, location):
2582
 
        """Set the target where this branch is bound to.
2583
 
 
2584
 
        :param location: URL to the target branch
2585
 
        """
2586
 
        if location:
2587
 
            self._transport.put_bytes('bound', location+'\n',
2588
 
                mode=self.bzrdir._get_file_mode())
2589
 
        else:
2590
 
            try:
2591
 
                self._transport.delete('bound')
2592
 
            except errors.NoSuchFile:
2593
 
                return False
2594
 
            return True
 
2606
    def unbind(self):
 
2607
        """If bound, unbind"""
 
2608
        return self.set_bound_location(None)
2595
2609
 
2596
2610
    @needs_write_lock
2597
2611
    def bind(self, other):
2619
2633
        # history around
2620
2634
        self.set_bound_location(other.base)
2621
2635
 
 
2636
    def get_bound_location(self):
 
2637
        try:
 
2638
            return self._transport.get_bytes('bound')[:-1]
 
2639
        except errors.NoSuchFile:
 
2640
            return None
 
2641
 
 
2642
    @needs_read_lock
 
2643
    def get_master_branch(self, possible_transports=None):
 
2644
        """Return the branch we are bound to.
 
2645
 
 
2646
        :return: Either a Branch, or None
 
2647
        """
 
2648
        if self._master_branch_cache is None:
 
2649
            self._master_branch_cache = self._get_master_branch(
 
2650
                possible_transports)
 
2651
        return self._master_branch_cache
 
2652
 
 
2653
    def _get_master_branch(self, possible_transports):
 
2654
        bound_loc = self.get_bound_location()
 
2655
        if not bound_loc:
 
2656
            return None
 
2657
        try:
 
2658
            return Branch.open(bound_loc,
 
2659
                               possible_transports=possible_transports)
 
2660
        except (errors.NotBranchError, errors.ConnectionError), e:
 
2661
            raise errors.BoundBranchConnectionFailure(
 
2662
                    self, bound_loc, e)
 
2663
 
2622
2664
    @needs_write_lock
2623
 
    def unbind(self):
2624
 
        """If bound, unbind"""
2625
 
        return self.set_bound_location(None)
 
2665
    def set_bound_location(self, location):
 
2666
        """Set the target where this branch is bound to.
 
2667
 
 
2668
        :param location: URL to the target branch
 
2669
        """
 
2670
        self._master_branch_cache = None
 
2671
        if location:
 
2672
            self._transport.put_bytes('bound', location+'\n',
 
2673
                mode=self.bzrdir._get_file_mode())
 
2674
        else:
 
2675
            try:
 
2676
                self._transport.delete('bound')
 
2677
            except errors.NoSuchFile:
 
2678
                return False
 
2679
            return True
2626
2680
 
2627
2681
    @needs_write_lock
2628
2682
    def update(self, possible_transports=None):
2641
2695
            return old_tip
2642
2696
        return None
2643
2697
 
2644
 
 
2645
 
class BzrBranch8(BzrBranch5):
 
2698
    def _read_last_revision_info(self):
 
2699
        revision_string = self._transport.get_bytes('last-revision')
 
2700
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
 
2701
        revision_id = cache_utf8.get_cached_utf8(revision_id)
 
2702
        revno = int(revno)
 
2703
        return revno, revision_id
 
2704
 
 
2705
    def _write_last_revision_info(self, revno, revision_id):
 
2706
        """Simply write out the revision id, with no checks.
 
2707
 
 
2708
        Use set_last_revision_info to perform this safely.
 
2709
 
 
2710
        Does not update the revision_history cache.
 
2711
        """
 
2712
        revision_id = _mod_revision.ensure_null(revision_id)
 
2713
        out_string = '%d %s\n' % (revno, revision_id)
 
2714
        self._transport.put_bytes('last-revision', out_string,
 
2715
            mode=self.bzrdir._get_file_mode())
 
2716
 
 
2717
 
 
2718
class FullHistoryBzrBranch(BzrBranch):
 
2719
    """Bzr branch which contains the full revision history."""
 
2720
 
 
2721
    @needs_write_lock
 
2722
    def set_last_revision_info(self, revno, revision_id):
 
2723
        if not revision_id or not isinstance(revision_id, basestring):
 
2724
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2725
        revision_id = _mod_revision.ensure_null(revision_id)
 
2726
        # this old format stores the full history, but this api doesn't
 
2727
        # provide it, so we must generate, and might as well check it's
 
2728
        # correct
 
2729
        history = self._lefthand_history(revision_id)
 
2730
        if len(history) != revno:
 
2731
            raise AssertionError('%d != %d' % (len(history), revno))
 
2732
        self._set_revision_history(history)
 
2733
 
 
2734
    def _read_last_revision_info(self):
 
2735
        rh = self._revision_history()
 
2736
        revno = len(rh)
 
2737
        if revno:
 
2738
            return (revno, rh[-1])
 
2739
        else:
 
2740
            return (0, _mod_revision.NULL_REVISION)
 
2741
 
 
2742
    @deprecated_method(deprecated_in((2, 4, 0)))
 
2743
    @needs_write_lock
 
2744
    def set_revision_history(self, rev_history):
 
2745
        """See Branch.set_revision_history."""
 
2746
        self._set_revision_history(rev_history)
 
2747
 
 
2748
    def _set_revision_history(self, rev_history):
 
2749
        if 'evil' in debug.debug_flags:
 
2750
            mutter_callsite(3, "set_revision_history scales with history.")
 
2751
        check_not_reserved_id = _mod_revision.check_not_reserved_id
 
2752
        for rev_id in rev_history:
 
2753
            check_not_reserved_id(rev_id)
 
2754
        if Branch.hooks['post_change_branch_tip']:
 
2755
            # Don't calculate the last_revision_info() if there are no hooks
 
2756
            # that will use it.
 
2757
            old_revno, old_revid = self.last_revision_info()
 
2758
        if len(rev_history) == 0:
 
2759
            revid = _mod_revision.NULL_REVISION
 
2760
        else:
 
2761
            revid = rev_history[-1]
 
2762
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
 
2763
        self._write_revision_history(rev_history)
 
2764
        self._clear_cached_state()
 
2765
        self._cache_revision_history(rev_history)
 
2766
        for hook in Branch.hooks['set_rh']:
 
2767
            hook(self, rev_history)
 
2768
        if Branch.hooks['post_change_branch_tip']:
 
2769
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2770
 
 
2771
    def _write_revision_history(self, history):
 
2772
        """Factored out of set_revision_history.
 
2773
 
 
2774
        This performs the actual writing to disk.
 
2775
        It is intended to be called by set_revision_history."""
 
2776
        self._transport.put_bytes(
 
2777
            'revision-history', '\n'.join(history),
 
2778
            mode=self.bzrdir._get_file_mode())
 
2779
 
 
2780
    def _gen_revision_history(self):
 
2781
        history = self._transport.get_bytes('revision-history').split('\n')
 
2782
        if history[-1:] == ['']:
 
2783
            # There shouldn't be a trailing newline, but just in case.
 
2784
            history.pop()
 
2785
        return history
 
2786
 
 
2787
    def _synchronize_history(self, destination, revision_id):
 
2788
        if not isinstance(destination, FullHistoryBzrBranch):
 
2789
            super(BzrBranch, self)._synchronize_history(
 
2790
                destination, revision_id)
 
2791
            return
 
2792
        if revision_id == _mod_revision.NULL_REVISION:
 
2793
            new_history = []
 
2794
        else:
 
2795
            new_history = self._revision_history()
 
2796
        if revision_id is not None and new_history != []:
 
2797
            try:
 
2798
                new_history = new_history[:new_history.index(revision_id) + 1]
 
2799
            except ValueError:
 
2800
                rev = self.repository.get_revision(revision_id)
 
2801
                new_history = rev.get_history(self.repository)[1:]
 
2802
        destination._set_revision_history(new_history)
 
2803
 
 
2804
    @needs_write_lock
 
2805
    def generate_revision_history(self, revision_id, last_rev=None,
 
2806
        other_branch=None):
 
2807
        """Create a new revision history that will finish with revision_id.
 
2808
 
 
2809
        :param revision_id: the new tip to use.
 
2810
        :param last_rev: The previous last_revision. If not None, then this
 
2811
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
2812
        :param other_branch: The other branch that DivergedBranches should
 
2813
            raise with respect to.
 
2814
        """
 
2815
        self._set_revision_history(self._lefthand_history(revision_id,
 
2816
            last_rev, other_branch))
 
2817
 
 
2818
 
 
2819
class BzrBranch5(FullHistoryBzrBranch):
 
2820
    """A format 5 branch. This supports new features over plain branches.
 
2821
 
 
2822
    It has support for a master_branch which is the data for bound branches.
 
2823
    """
 
2824
 
 
2825
 
 
2826
class BzrBranch8(BzrBranch):
2646
2827
    """A branch that stores tree-reference locations."""
2647
2828
 
2648
 
    def _open_hook(self):
 
2829
    def _open_hook(self, possible_transports=None):
2649
2830
        if self._ignore_fallbacks:
2650
2831
            return
 
2832
        if possible_transports is None:
 
2833
            possible_transports = [self.bzrdir.root_transport]
2651
2834
        try:
2652
2835
            url = self.get_stacked_on_url()
2653
2836
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2661
2844
                    raise AssertionError(
2662
2845
                        "'transform_fallback_location' hook %s returned "
2663
2846
                        "None, not a URL." % hook_name)
2664
 
            self._activate_fallback_location(url)
 
2847
            self._activate_fallback_location(url,
 
2848
                possible_transports=possible_transports)
2665
2849
 
2666
2850
    def __init__(self, *args, **kwargs):
2667
2851
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2674
2858
        self._last_revision_info_cache = None
2675
2859
        self._reference_info = None
2676
2860
 
2677
 
    def _last_revision_info(self):
2678
 
        revision_string = self._transport.get_bytes('last-revision')
2679
 
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2680
 
        revision_id = cache_utf8.get_cached_utf8(revision_id)
2681
 
        revno = int(revno)
2682
 
        return revno, revision_id
2683
 
 
2684
 
    def _write_last_revision_info(self, revno, revision_id):
2685
 
        """Simply write out the revision id, with no checks.
2686
 
 
2687
 
        Use set_last_revision_info to perform this safely.
2688
 
 
2689
 
        Does not update the revision_history cache.
2690
 
        Intended to be called by set_last_revision_info and
2691
 
        _write_revision_history.
2692
 
        """
2693
 
        revision_id = _mod_revision.ensure_null(revision_id)
2694
 
        out_string = '%d %s\n' % (revno, revision_id)
2695
 
        self._transport.put_bytes('last-revision', out_string,
2696
 
            mode=self.bzrdir._get_file_mode())
2697
 
 
2698
 
    @needs_write_lock
2699
 
    def set_last_revision_info(self, revno, revision_id):
2700
 
        revision_id = _mod_revision.ensure_null(revision_id)
2701
 
        old_revno, old_revid = self.last_revision_info()
2702
 
        if self._get_append_revisions_only():
2703
 
            self._check_history_violation(revision_id)
2704
 
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2705
 
        self._write_last_revision_info(revno, revision_id)
2706
 
        self._clear_cached_state()
2707
 
        self._last_revision_info_cache = revno, revision_id
2708
 
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2709
 
 
2710
 
    def _synchronize_history(self, destination, revision_id):
2711
 
        """Synchronize last revision and revision history between branches.
2712
 
 
2713
 
        :see: Branch._synchronize_history
2714
 
        """
2715
 
        # XXX: The base Branch has a fast implementation of this method based
2716
 
        # on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2717
 
        # that uses set_revision_history.  This class inherits from BzrBranch5,
2718
 
        # but wants the fast implementation, so it calls
2719
 
        # Branch._synchronize_history directly.
2720
 
        Branch._synchronize_history(self, destination, revision_id)
2721
 
 
2722
2861
    def _check_history_violation(self, revision_id):
2723
 
        last_revision = _mod_revision.ensure_null(self.last_revision())
 
2862
        current_revid = self.last_revision()
 
2863
        last_revision = _mod_revision.ensure_null(current_revid)
2724
2864
        if _mod_revision.is_null(last_revision):
2725
2865
            return
2726
 
        if last_revision not in self._lefthand_history(revision_id):
2727
 
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
 
2866
        graph = self.repository.get_graph()
 
2867
        for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
 
2868
            if lh_ancestor == current_revid:
 
2869
                return
 
2870
        raise errors.AppendRevisionsOnlyViolation(self.user_url)
2728
2871
 
2729
2872
    def _gen_revision_history(self):
2730
2873
        """Generate the revision history from last revision
2733
2876
        self._extend_partial_history(stop_index=last_revno-1)
2734
2877
        return list(reversed(self._partial_revision_history_cache))
2735
2878
 
2736
 
    def _write_revision_history(self, history):
2737
 
        """Factored out of set_revision_history.
2738
 
 
2739
 
        This performs the actual writing to disk, with format-specific checks.
2740
 
        It is intended to be called by BzrBranch5.set_revision_history.
2741
 
        """
2742
 
        if len(history) == 0:
2743
 
            last_revision = 'null:'
2744
 
        else:
2745
 
            if history != self._lefthand_history(history[-1]):
2746
 
                raise errors.NotLefthandHistory(history)
2747
 
            last_revision = history[-1]
2748
 
        if self._get_append_revisions_only():
2749
 
            self._check_history_violation(last_revision)
2750
 
        self._write_last_revision_info(len(history), last_revision)
2751
 
 
2752
2879
    @needs_write_lock
2753
2880
    def _set_parent_location(self, url):
2754
2881
        """Set the parent branch"""
2840
2967
 
2841
2968
    def set_bound_location(self, location):
2842
2969
        """See Branch.set_push_location."""
 
2970
        self._master_branch_cache = None
2843
2971
        result = None
2844
2972
        config = self.get_config()
2845
2973
        if location is None:
2876
3004
        # you can always ask for the URL; but you might not be able to use it
2877
3005
        # if the repo can't support stacking.
2878
3006
        ## self._check_stackable_repo()
2879
 
        stacked_url = self._get_config_location('stacked_on_location')
 
3007
        # stacked_on_location is only ever defined in branch.conf, so don't
 
3008
        # waste effort reading the whole stack of config files.
 
3009
        config = self.get_config()._get_branch_data_config()
 
3010
        stacked_url = self._get_config_location('stacked_on_location',
 
3011
            config=config)
2880
3012
        if stacked_url is None:
2881
3013
            raise errors.NotStacked(self)
2882
3014
        return stacked_url
2883
3015
 
2884
 
    def _get_append_revisions_only(self):
2885
 
        return self.get_config(
2886
 
            ).get_user_option_as_bool('append_revisions_only')
2887
 
 
2888
 
    @needs_write_lock
2889
 
    def generate_revision_history(self, revision_id, last_rev=None,
2890
 
                                  other_branch=None):
2891
 
        """See BzrBranch5.generate_revision_history"""
2892
 
        history = self._lefthand_history(revision_id, last_rev, other_branch)
2893
 
        revno = len(history)
2894
 
        self.set_last_revision_info(revno, revision_id)
2895
 
 
2896
3016
    @needs_read_lock
2897
3017
    def get_rev_id(self, revno, history=None):
2898
3018
        """Find the revision id of the specified revno."""
2922
3042
        try:
2923
3043
            index = self._partial_revision_history_cache.index(revision_id)
2924
3044
        except ValueError:
2925
 
            self._extend_partial_history(stop_revision=revision_id)
 
3045
            try:
 
3046
                self._extend_partial_history(stop_revision=revision_id)
 
3047
            except errors.RevisionNotPresent, e:
 
3048
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
2926
3049
            index = len(self._partial_revision_history_cache) - 1
 
3050
            if index < 0:
 
3051
                raise errors.NoSuchRevision(self, revision_id)
2927
3052
            if self._partial_revision_history_cache[index] != revision_id:
2928
3053
                raise errors.NoSuchRevision(self, revision_id)
2929
3054
        return self.revno() - index
2981
3106
    :ivar local_branch: target branch if there is a Master, else None
2982
3107
    :ivar target_branch: Target/destination branch object. (write locked)
2983
3108
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
 
3109
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
2984
3110
    """
2985
3111
 
 
3112
    @deprecated_method(deprecated_in((2, 3, 0)))
2986
3113
    def __int__(self):
2987
 
        # DEPRECATED: pull used to return the change in revno
 
3114
        """Return the relative change in revno.
 
3115
 
 
3116
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3117
        """
2988
3118
        return self.new_revno - self.old_revno
2989
3119
 
2990
3120
    def report(self, to_file):
 
3121
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
3122
        tag_updates = getattr(self, "tag_updates", None)
2991
3123
        if not is_quiet():
2992
 
            if self.old_revid == self.new_revid:
2993
 
                to_file.write('No revisions to pull.\n')
2994
 
            else:
 
3124
            if self.old_revid != self.new_revid:
2995
3125
                to_file.write('Now on revision %d.\n' % self.new_revno)
 
3126
            if tag_updates:
 
3127
                to_file.write('%d tag(s) updated.\n' % len(tag_updates))
 
3128
            if self.old_revid == self.new_revid and not tag_updates:
 
3129
                if not tag_conflicts:
 
3130
                    to_file.write('No revisions or tags to pull.\n')
 
3131
                else:
 
3132
                    to_file.write('No revisions to pull.\n')
2996
3133
        self._show_tag_conficts(to_file)
2997
3134
 
2998
3135
 
3015
3152
        target, otherwise it will be None.
3016
3153
    """
3017
3154
 
 
3155
    @deprecated_method(deprecated_in((2, 3, 0)))
3018
3156
    def __int__(self):
3019
 
        # DEPRECATED: push used to return the change in revno
 
3157
        """Return the relative change in revno.
 
3158
 
 
3159
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3160
        """
3020
3161
        return self.new_revno - self.old_revno
3021
3162
 
3022
3163
    def report(self, to_file):
3023
 
        """Write a human-readable description of the result."""
3024
 
        if self.old_revid == self.new_revid:
3025
 
            note('No new revisions to push.')
3026
 
        else:
3027
 
            note('Pushed up to revision %d.' % self.new_revno)
 
3164
        # TODO: This function gets passed a to_file, but then
 
3165
        # ignores it and calls note() instead. This is also
 
3166
        # inconsistent with PullResult(), which writes to stdout.
 
3167
        # -- JRV20110901, bug #838853
 
3168
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
3169
        tag_updates = getattr(self, "tag_updates", None)
 
3170
        if not is_quiet():
 
3171
            if self.old_revid != self.new_revid:
 
3172
                note(gettext('Pushed up to revision %d.') % self.new_revno)
 
3173
            if tag_updates:
 
3174
                note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
 
3175
            if self.old_revid == self.new_revid and not tag_updates:
 
3176
                if not tag_conflicts:
 
3177
                    note(gettext('No new revisions or tags to push.'))
 
3178
                else:
 
3179
                    note(gettext('No new revisions to push.'))
3028
3180
        self._show_tag_conficts(to_file)
3029
3181
 
3030
3182
 
3044
3196
        :param verbose: Requests more detailed display of what was checked,
3045
3197
            if any.
3046
3198
        """
3047
 
        note('checked branch %s format %s', self.branch.user_url,
3048
 
            self.branch._format)
 
3199
        note(gettext('checked branch {0} format {1}').format(
 
3200
                                self.branch.user_url, self.branch._format))
3049
3201
        for error in self.errors:
3050
 
            note('found error:%s', error)
 
3202
            note(gettext('found error:%s'), error)
3051
3203
 
3052
3204
 
3053
3205
class Converter5to6(object):
3092
3244
 
3093
3245
 
3094
3246
class Converter7to8(object):
3095
 
    """Perform an in-place upgrade of format 6 to format 7"""
 
3247
    """Perform an in-place upgrade of format 7 to format 8"""
3096
3248
 
3097
3249
    def convert(self, branch):
3098
3250
        format = BzrBranchFormat8()
3101
3253
        branch._transport.put_bytes('format', format.get_format_string())
3102
3254
 
3103
3255
 
3104
 
def _run_with_write_locked_target(target, callable, *args, **kwargs):
3105
 
    """Run ``callable(*args, **kwargs)``, write-locking target for the
3106
 
    duration.
3107
 
 
3108
 
    _run_with_write_locked_target will attempt to release the lock it acquires.
3109
 
 
3110
 
    If an exception is raised by callable, then that exception *will* be
3111
 
    propagated, even if the unlock attempt raises its own error.  Thus
3112
 
    _run_with_write_locked_target should be preferred to simply doing::
3113
 
 
3114
 
        target.lock_write()
3115
 
        try:
3116
 
            return callable(*args, **kwargs)
3117
 
        finally:
3118
 
            target.unlock()
3119
 
 
3120
 
    """
3121
 
    # This is very similar to bzrlib.decorators.needs_write_lock.  Perhaps they
3122
 
    # should share code?
3123
 
    target.lock_write()
3124
 
    try:
3125
 
        result = callable(*args, **kwargs)
3126
 
    except:
3127
 
        exc_info = sys.exc_info()
3128
 
        try:
3129
 
            target.unlock()
3130
 
        finally:
3131
 
            raise exc_info[0], exc_info[1], exc_info[2]
3132
 
    else:
3133
 
        target.unlock()
3134
 
        return result
3135
 
 
3136
 
 
3137
3256
class InterBranch(InterObject):
3138
3257
    """This class represents operations taking place between two branches.
3139
3258
 
3145
3264
    _optimisers = []
3146
3265
    """The available optimised InterBranch types."""
3147
3266
 
3148
 
    @staticmethod
3149
 
    def _get_branch_formats_to_test():
3150
 
        """Return a tuple with the Branch formats to use when testing."""
3151
 
        raise NotImplementedError(InterBranch._get_branch_formats_to_test)
 
3267
    @classmethod
 
3268
    def _get_branch_formats_to_test(klass):
 
3269
        """Return an iterable of format tuples for testing.
 
3270
        
 
3271
        :return: An iterable of (from_format, to_format) to use when testing
 
3272
            this InterBranch class. Each InterBranch class should define this
 
3273
            method itself.
 
3274
        """
 
3275
        raise NotImplementedError(klass._get_branch_formats_to_test)
3152
3276
 
 
3277
    @needs_write_lock
3153
3278
    def pull(self, overwrite=False, stop_revision=None,
3154
3279
             possible_transports=None, local=False):
3155
3280
        """Mirror source into target branch.
3160
3285
        """
3161
3286
        raise NotImplementedError(self.pull)
3162
3287
 
3163
 
    def update_revisions(self, stop_revision=None, overwrite=False,
3164
 
                         graph=None):
3165
 
        """Pull in new perfect-fit revisions.
3166
 
 
3167
 
        :param stop_revision: Updated until the given revision
3168
 
        :param overwrite: Always set the branch pointer, rather than checking
3169
 
            to see if it is a proper descendant.
3170
 
        :param graph: A Graph object that can be used to query history
3171
 
            information. This can be None.
3172
 
        :return: None
3173
 
        """
3174
 
        raise NotImplementedError(self.update_revisions)
3175
 
 
3176
 
    def push(self, overwrite=False, stop_revision=None,
 
3288
    @needs_write_lock
 
3289
    def push(self, overwrite=False, stop_revision=None, lossy=False,
3177
3290
             _override_hook_source_branch=None):
3178
3291
        """Mirror the source branch into the target branch.
3179
3292
 
3181
3294
        """
3182
3295
        raise NotImplementedError(self.push)
3183
3296
 
 
3297
    @needs_write_lock
 
3298
    def copy_content_into(self, revision_id=None):
 
3299
        """Copy the content of source into target
 
3300
 
 
3301
        revision_id: if not None, the revision history in the new branch will
 
3302
                     be truncated to end with revision_id.
 
3303
        """
 
3304
        raise NotImplementedError(self.copy_content_into)
 
3305
 
 
3306
    @needs_write_lock
 
3307
    def fetch(self, stop_revision=None, limit=None):
 
3308
        """Fetch revisions.
 
3309
 
 
3310
        :param stop_revision: Last revision to fetch
 
3311
        :param limit: Optional rough limit of revisions to fetch
 
3312
        """
 
3313
        raise NotImplementedError(self.fetch)
 
3314
 
3184
3315
 
3185
3316
class GenericInterBranch(InterBranch):
3186
 
    """InterBranch implementation that uses public Branch functions.
3187
 
    """
3188
 
 
3189
 
    @staticmethod
3190
 
    def _get_branch_formats_to_test():
3191
 
        return BranchFormat._default_format, BranchFormat._default_format
3192
 
 
3193
 
    def update_revisions(self, stop_revision=None, overwrite=False,
3194
 
        graph=None):
3195
 
        """See InterBranch.update_revisions()."""
 
3317
    """InterBranch implementation that uses public Branch functions."""
 
3318
 
 
3319
    @classmethod
 
3320
    def is_compatible(klass, source, target):
 
3321
        # GenericBranch uses the public API, so always compatible
 
3322
        return True
 
3323
 
 
3324
    @classmethod
 
3325
    def _get_branch_formats_to_test(klass):
 
3326
        return [(format_registry.get_default(), format_registry.get_default())]
 
3327
 
 
3328
    @classmethod
 
3329
    def unwrap_format(klass, format):
 
3330
        if isinstance(format, remote.RemoteBranchFormat):
 
3331
            format._ensure_real()
 
3332
            return format._custom_format
 
3333
        return format
 
3334
 
 
3335
    @needs_write_lock
 
3336
    def copy_content_into(self, revision_id=None):
 
3337
        """Copy the content of source into target
 
3338
 
 
3339
        revision_id: if not None, the revision history in the new branch will
 
3340
                     be truncated to end with revision_id.
 
3341
        """
 
3342
        self.source.update_references(self.target)
 
3343
        self.source._synchronize_history(self.target, revision_id)
 
3344
        try:
 
3345
            parent = self.source.get_parent()
 
3346
        except errors.InaccessibleParent, e:
 
3347
            mutter('parent was not accessible to copy: %s', e)
 
3348
        else:
 
3349
            if parent:
 
3350
                self.target.set_parent(parent)
 
3351
        if self.source._push_should_merge_tags():
 
3352
            self.source.tags.merge_to(self.target.tags)
 
3353
 
 
3354
    @needs_write_lock
 
3355
    def fetch(self, stop_revision=None, limit=None):
 
3356
        if self.target.base == self.source.base:
 
3357
            return (0, [])
3196
3358
        self.source.lock_read()
3197
3359
        try:
3198
 
            other_revno, other_last_revision = self.source.last_revision_info()
3199
 
            stop_revno = None # unknown
3200
 
            if stop_revision is None:
3201
 
                stop_revision = other_last_revision
3202
 
                if _mod_revision.is_null(stop_revision):
3203
 
                    # if there are no commits, we're done.
3204
 
                    return
3205
 
                stop_revno = other_revno
3206
 
 
3207
 
            # what's the current last revision, before we fetch [and change it
3208
 
            # possibly]
3209
 
            last_rev = _mod_revision.ensure_null(self.target.last_revision())
3210
 
            # we fetch here so that we don't process data twice in the common
3211
 
            # case of having something to pull, and so that the check for
3212
 
            # already merged can operate on the just fetched graph, which will
3213
 
            # be cached in memory.
3214
 
            self.target.fetch(self.source, stop_revision)
3215
 
            # Check to see if one is an ancestor of the other
3216
 
            if not overwrite:
3217
 
                if graph is None:
3218
 
                    graph = self.target.repository.get_graph()
3219
 
                if self.target._check_if_descendant_or_diverged(
3220
 
                        stop_revision, last_rev, graph, self.source):
3221
 
                    # stop_revision is a descendant of last_rev, but we aren't
3222
 
                    # overwriting, so we're done.
3223
 
                    return
3224
 
            if stop_revno is None:
3225
 
                if graph is None:
3226
 
                    graph = self.target.repository.get_graph()
3227
 
                this_revno, this_last_revision = \
3228
 
                        self.target.last_revision_info()
3229
 
                stop_revno = graph.find_distance_to_null(stop_revision,
3230
 
                                [(other_last_revision, other_revno),
3231
 
                                 (this_last_revision, this_revno)])
3232
 
            self.target.set_last_revision_info(stop_revno, stop_revision)
 
3360
            fetch_spec_factory = fetch.FetchSpecFactory()
 
3361
            fetch_spec_factory.source_branch = self.source
 
3362
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
 
3363
            fetch_spec_factory.source_repo = self.source.repository
 
3364
            fetch_spec_factory.target_repo = self.target.repository
 
3365
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
 
3366
            fetch_spec_factory.limit = limit
 
3367
            fetch_spec = fetch_spec_factory.make_fetch_spec()
 
3368
            return self.target.repository.fetch(self.source.repository,
 
3369
                fetch_spec=fetch_spec)
3233
3370
        finally:
3234
3371
            self.source.unlock()
3235
3372
 
 
3373
    @needs_write_lock
 
3374
    def _update_revisions(self, stop_revision=None, overwrite=False,
 
3375
            graph=None):
 
3376
        other_revno, other_last_revision = self.source.last_revision_info()
 
3377
        stop_revno = None # unknown
 
3378
        if stop_revision is None:
 
3379
            stop_revision = other_last_revision
 
3380
            if _mod_revision.is_null(stop_revision):
 
3381
                # if there are no commits, we're done.
 
3382
                return
 
3383
            stop_revno = other_revno
 
3384
 
 
3385
        # what's the current last revision, before we fetch [and change it
 
3386
        # possibly]
 
3387
        last_rev = _mod_revision.ensure_null(self.target.last_revision())
 
3388
        # we fetch here so that we don't process data twice in the common
 
3389
        # case of having something to pull, and so that the check for
 
3390
        # already merged can operate on the just fetched graph, which will
 
3391
        # be cached in memory.
 
3392
        self.fetch(stop_revision=stop_revision)
 
3393
        # Check to see if one is an ancestor of the other
 
3394
        if not overwrite:
 
3395
            if graph is None:
 
3396
                graph = self.target.repository.get_graph()
 
3397
            if self.target._check_if_descendant_or_diverged(
 
3398
                    stop_revision, last_rev, graph, self.source):
 
3399
                # stop_revision is a descendant of last_rev, but we aren't
 
3400
                # overwriting, so we're done.
 
3401
                return
 
3402
        if stop_revno is None:
 
3403
            if graph is None:
 
3404
                graph = self.target.repository.get_graph()
 
3405
            this_revno, this_last_revision = \
 
3406
                    self.target.last_revision_info()
 
3407
            stop_revno = graph.find_distance_to_null(stop_revision,
 
3408
                            [(other_last_revision, other_revno),
 
3409
                             (this_last_revision, this_revno)])
 
3410
        self.target.set_last_revision_info(stop_revno, stop_revision)
 
3411
 
 
3412
    @needs_write_lock
3236
3413
    def pull(self, overwrite=False, stop_revision=None,
 
3414
             possible_transports=None, run_hooks=True,
 
3415
             _override_hook_target=None, local=False):
 
3416
        """Pull from source into self, updating my master if any.
 
3417
 
 
3418
        :param run_hooks: Private parameter - if false, this branch
 
3419
            is being called because it's the master of the primary branch,
 
3420
            so it should not run its hooks.
 
3421
        """
 
3422
        bound_location = self.target.get_bound_location()
 
3423
        if local and not bound_location:
 
3424
            raise errors.LocalRequiresBoundBranch()
 
3425
        master_branch = None
 
3426
        source_is_master = False
 
3427
        if bound_location:
 
3428
            # bound_location comes from a config file, some care has to be
 
3429
            # taken to relate it to source.user_url
 
3430
            normalized = urlutils.normalize_url(bound_location)
 
3431
            try:
 
3432
                relpath = self.source.user_transport.relpath(normalized)
 
3433
                source_is_master = (relpath == '')
 
3434
            except (errors.PathNotChild, errors.InvalidURL):
 
3435
                source_is_master = False
 
3436
        if not local and bound_location and not source_is_master:
 
3437
            # not pulling from master, so we need to update master.
 
3438
            master_branch = self.target.get_master_branch(possible_transports)
 
3439
            master_branch.lock_write()
 
3440
        try:
 
3441
            if master_branch:
 
3442
                # pull from source into master.
 
3443
                master_branch.pull(self.source, overwrite, stop_revision,
 
3444
                    run_hooks=False)
 
3445
            return self._pull(overwrite,
 
3446
                stop_revision, _hook_master=master_branch,
 
3447
                run_hooks=run_hooks,
 
3448
                _override_hook_target=_override_hook_target,
 
3449
                merge_tags_to_master=not source_is_master)
 
3450
        finally:
 
3451
            if master_branch:
 
3452
                master_branch.unlock()
 
3453
 
 
3454
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
3455
             _override_hook_source_branch=None):
 
3456
        """See InterBranch.push.
 
3457
 
 
3458
        This is the basic concrete implementation of push()
 
3459
 
 
3460
        :param _override_hook_source_branch: If specified, run the hooks
 
3461
            passing this Branch as the source, rather than self.  This is for
 
3462
            use of RemoteBranch, where push is delegated to the underlying
 
3463
            vfs-based Branch.
 
3464
        """
 
3465
        if lossy:
 
3466
            raise errors.LossyPushToSameVCS(self.source, self.target)
 
3467
        # TODO: Public option to disable running hooks - should be trivial but
 
3468
        # needs tests.
 
3469
 
 
3470
        op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
 
3471
        op.add_cleanup(self.source.lock_read().unlock)
 
3472
        op.add_cleanup(self.target.lock_write().unlock)
 
3473
        return op.run(overwrite, stop_revision,
 
3474
            _override_hook_source_branch=_override_hook_source_branch)
 
3475
 
 
3476
    def _basic_push(self, overwrite, stop_revision):
 
3477
        """Basic implementation of push without bound branches or hooks.
 
3478
 
 
3479
        Must be called with source read locked and target write locked.
 
3480
        """
 
3481
        result = BranchPushResult()
 
3482
        result.source_branch = self.source
 
3483
        result.target_branch = self.target
 
3484
        result.old_revno, result.old_revid = self.target.last_revision_info()
 
3485
        self.source.update_references(self.target)
 
3486
        if result.old_revid != stop_revision:
 
3487
            # We assume that during 'push' this repository is closer than
 
3488
            # the target.
 
3489
            graph = self.source.repository.get_graph(self.target.repository)
 
3490
            self._update_revisions(stop_revision, overwrite=overwrite,
 
3491
                    graph=graph)
 
3492
        if self.source._push_should_merge_tags():
 
3493
            result.tag_updates, result.tag_conflicts = (
 
3494
                self.source.tags.merge_to(self.target.tags, overwrite))
 
3495
        result.new_revno, result.new_revid = self.target.last_revision_info()
 
3496
        return result
 
3497
 
 
3498
    def _push_with_bound_branches(self, operation, overwrite, stop_revision,
 
3499
            _override_hook_source_branch=None):
 
3500
        """Push from source into target, and into target's master if any.
 
3501
        """
 
3502
        def _run_hooks():
 
3503
            if _override_hook_source_branch:
 
3504
                result.source_branch = _override_hook_source_branch
 
3505
            for hook in Branch.hooks['post_push']:
 
3506
                hook(result)
 
3507
 
 
3508
        bound_location = self.target.get_bound_location()
 
3509
        if bound_location and self.target.base != bound_location:
 
3510
            # there is a master branch.
 
3511
            #
 
3512
            # XXX: Why the second check?  Is it even supported for a branch to
 
3513
            # be bound to itself? -- mbp 20070507
 
3514
            master_branch = self.target.get_master_branch()
 
3515
            master_branch.lock_write()
 
3516
            operation.add_cleanup(master_branch.unlock)
 
3517
            # push into the master from the source branch.
 
3518
            master_inter = InterBranch.get(self.source, master_branch)
 
3519
            master_inter._basic_push(overwrite, stop_revision)
 
3520
            # and push into the target branch from the source. Note that
 
3521
            # we push from the source branch again, because it's considered
 
3522
            # the highest bandwidth repository.
 
3523
            result = self._basic_push(overwrite, stop_revision)
 
3524
            result.master_branch = master_branch
 
3525
            result.local_branch = self.target
 
3526
        else:
 
3527
            master_branch = None
 
3528
            # no master branch
 
3529
            result = self._basic_push(overwrite, stop_revision)
 
3530
            # TODO: Why set master_branch and local_branch if there's no
 
3531
            # binding?  Maybe cleaner to just leave them unset? -- mbp
 
3532
            # 20070504
 
3533
            result.master_branch = self.target
 
3534
            result.local_branch = None
 
3535
        _run_hooks()
 
3536
        return result
 
3537
 
 
3538
    def _pull(self, overwrite=False, stop_revision=None,
3237
3539
             possible_transports=None, _hook_master=None, run_hooks=True,
3238
 
             _override_hook_target=None, local=False):
 
3540
             _override_hook_target=None, local=False,
 
3541
             merge_tags_to_master=True):
3239
3542
        """See Branch.pull.
3240
3543
 
 
3544
        This function is the core worker, used by GenericInterBranch.pull to
 
3545
        avoid duplication when pulling source->master and source->local.
 
3546
 
3241
3547
        :param _hook_master: Private parameter - set the branch to
3242
3548
            be supplied as the master to pull hooks.
3243
3549
        :param run_hooks: Private parameter - if false, this branch
3244
3550
            is being called because it's the master of the primary branch,
3245
3551
            so it should not run its hooks.
 
3552
            is being called because it's the master of the primary branch,
 
3553
            so it should not run its hooks.
3246
3554
        :param _override_hook_target: Private parameter - set the branch to be
3247
3555
            supplied as the target_branch to pull hooks.
3248
3556
        :param local: Only update the local branch, and not the bound branch.
3267
3575
            # -- JRV20090506
3268
3576
            result.old_revno, result.old_revid = \
3269
3577
                self.target.last_revision_info()
3270
 
            self.target.update_revisions(self.source, stop_revision,
3271
 
                overwrite=overwrite, graph=graph)
 
3578
            self._update_revisions(stop_revision, overwrite=overwrite,
 
3579
                graph=graph)
3272
3580
            # TODO: The old revid should be specified when merging tags, 
3273
3581
            # so a tags implementation that versions tags can only 
3274
3582
            # pull in the most recent changes. -- JRV20090506
3275
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3276
 
                overwrite)
 
3583
            result.tag_updates, result.tag_conflicts = (
 
3584
                self.source.tags.merge_to(self.target.tags, overwrite,
 
3585
                    ignore_master=not merge_tags_to_master))
3277
3586
            result.new_revno, result.new_revid = self.target.last_revision_info()
3278
3587
            if _hook_master:
3279
3588
                result.master_branch = _hook_master
3288
3597
            self.source.unlock()
3289
3598
        return result
3290
3599
 
3291
 
    def push(self, overwrite=False, stop_revision=None,
3292
 
             _override_hook_source_branch=None):
3293
 
        """See InterBranch.push.
3294
 
 
3295
 
        This is the basic concrete implementation of push()
3296
 
 
3297
 
        :param _override_hook_source_branch: If specified, run
3298
 
        the hooks passing this Branch as the source, rather than self.
3299
 
        This is for use of RemoteBranch, where push is delegated to the
3300
 
        underlying vfs-based Branch.
3301
 
        """
3302
 
        # TODO: Public option to disable running hooks - should be trivial but
3303
 
        # needs tests.
3304
 
        self.source.lock_read()
3305
 
        try:
3306
 
            return _run_with_write_locked_target(
3307
 
                self.target, self._push_with_bound_branches, overwrite,
3308
 
                stop_revision,
3309
 
                _override_hook_source_branch=_override_hook_source_branch)
3310
 
        finally:
3311
 
            self.source.unlock()
3312
 
 
3313
 
    def _push_with_bound_branches(self, overwrite, stop_revision,
3314
 
            _override_hook_source_branch=None):
3315
 
        """Push from source into target, and into target's master if any.
3316
 
        """
3317
 
        def _run_hooks():
3318
 
            if _override_hook_source_branch:
3319
 
                result.source_branch = _override_hook_source_branch
3320
 
            for hook in Branch.hooks['post_push']:
3321
 
                hook(result)
3322
 
 
3323
 
        bound_location = self.target.get_bound_location()
3324
 
        if bound_location and self.target.base != bound_location:
3325
 
            # there is a master branch.
3326
 
            #
3327
 
            # XXX: Why the second check?  Is it even supported for a branch to
3328
 
            # be bound to itself? -- mbp 20070507
3329
 
            master_branch = self.target.get_master_branch()
3330
 
            master_branch.lock_write()
3331
 
            try:
3332
 
                # push into the master from the source branch.
3333
 
                self.source._basic_push(master_branch, overwrite, stop_revision)
3334
 
                # and push into the target branch from the source. Note that we
3335
 
                # push from the source branch again, because its considered the
3336
 
                # highest bandwidth repository.
3337
 
                result = self.source._basic_push(self.target, overwrite,
3338
 
                    stop_revision)
3339
 
                result.master_branch = master_branch
3340
 
                result.local_branch = self.target
3341
 
                _run_hooks()
3342
 
                return result
3343
 
            finally:
3344
 
                master_branch.unlock()
3345
 
        else:
3346
 
            # no master branch
3347
 
            result = self.source._basic_push(self.target, overwrite,
3348
 
                stop_revision)
3349
 
            # TODO: Why set master_branch and local_branch if there's no
3350
 
            # binding?  Maybe cleaner to just leave them unset? -- mbp
3351
 
            # 20070504
3352
 
            result.master_branch = self.target
3353
 
            result.local_branch = None
3354
 
            _run_hooks()
3355
 
            return result
3356
 
 
3357
 
    @classmethod
3358
 
    def is_compatible(self, source, target):
3359
 
        # GenericBranch uses the public API, so always compatible
3360
 
        return True
3361
 
 
3362
 
 
3363
 
class InterToBranch5(GenericInterBranch):
3364
 
 
3365
 
    @staticmethod
3366
 
    def _get_branch_formats_to_test():
3367
 
        return BranchFormat._default_format, BzrBranchFormat5()
3368
 
 
3369
 
    def pull(self, overwrite=False, stop_revision=None,
3370
 
             possible_transports=None, run_hooks=True,
3371
 
             _override_hook_target=None, local=False):
3372
 
        """Pull from source into self, updating my master if any.
3373
 
 
3374
 
        :param run_hooks: Private parameter - if false, this branch
3375
 
            is being called because it's the master of the primary branch,
3376
 
            so it should not run its hooks.
3377
 
        """
3378
 
        bound_location = self.target.get_bound_location()
3379
 
        if local and not bound_location:
3380
 
            raise errors.LocalRequiresBoundBranch()
3381
 
        master_branch = None
3382
 
        if not local and bound_location and self.source.user_url != bound_location:
3383
 
            # not pulling from master, so we need to update master.
3384
 
            master_branch = self.target.get_master_branch(possible_transports)
3385
 
            master_branch.lock_write()
3386
 
        try:
3387
 
            if master_branch:
3388
 
                # pull from source into master.
3389
 
                master_branch.pull(self.source, overwrite, stop_revision,
3390
 
                    run_hooks=False)
3391
 
            return super(InterToBranch5, self).pull(overwrite,
3392
 
                stop_revision, _hook_master=master_branch,
3393
 
                run_hooks=run_hooks,
3394
 
                _override_hook_target=_override_hook_target)
3395
 
        finally:
3396
 
            if master_branch:
3397
 
                master_branch.unlock()
3398
 
 
3399
3600
 
3400
3601
InterBranch.register_optimiser(GenericInterBranch)
3401
 
InterBranch.register_optimiser(InterToBranch5)