~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

(jameinel) Allow 'bzr serve' to interpret SIGHUP as a graceful shutdown.
 (bug #795025) (John A Meinel)

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
16
16
 
17
17
 
18
18
from cStringIO import StringIO
19
 
import sys
20
19
 
21
20
from bzrlib.lazy_import import lazy_import
22
21
lazy_import(globals(), """
23
 
from itertools import chain
 
22
import itertools
24
23
from bzrlib import (
25
24
        bzrdir,
26
25
        cache_utf8,
 
26
        cleanup,
27
27
        config as _mod_config,
28
28
        debug,
29
29
        errors,
 
30
        fetch,
 
31
        graph as _mod_graph,
30
32
        lockdir,
31
33
        lockable_files,
 
34
        remote,
32
35
        repository,
33
36
        revision as _mod_revision,
34
37
        rio,
35
 
        symbol_versioning,
 
38
        tag as _mod_tag,
36
39
        transport,
37
 
        tsort,
38
40
        ui,
39
41
        urlutils,
40
42
        )
41
 
from bzrlib.config import BranchConfig, TransportConfig
42
 
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
43
 
from bzrlib.tag import (
44
 
    BasicTags,
45
 
    DisabledTags,
46
 
    )
 
43
from bzrlib.i18n import gettext, ngettext
47
44
""")
48
45
 
49
 
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
50
 
from bzrlib.hooks import HookPoint, Hooks
 
46
from bzrlib import (
 
47
    controldir,
 
48
    )
 
49
from bzrlib.decorators import (
 
50
    needs_read_lock,
 
51
    needs_write_lock,
 
52
    only_raises,
 
53
    )
 
54
from bzrlib.hooks import Hooks
51
55
from bzrlib.inter import InterObject
52
56
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
53
57
from bzrlib import registry
58
62
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
59
63
 
60
64
 
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):
 
65
class Branch(controldir.ControlComponent):
67
66
    """Branch holding a history of revisions.
68
67
 
69
68
    :ivar base:
70
69
        Base directory/url of the branch; using control_url and
71
70
        control_transport is more standardized.
72
 
 
73
 
    hooks: An instance of BranchHooks.
 
71
    :ivar hooks: An instance of BranchHooks.
 
72
    :ivar _master_branch_cache: cached result of get_master_branch, see
 
73
        _clear_cached_state.
74
74
    """
75
75
    # this is really an instance variable - FIXME move it there
76
76
    # - RBC 20060112
90
90
        self._revision_id_to_revno_cache = None
91
91
        self._partial_revision_id_to_revno_cache = {}
92
92
        self._partial_revision_history_cache = []
 
93
        self._tags_bytes = None
93
94
        self._last_revision_info_cache = None
 
95
        self._master_branch_cache = None
94
96
        self._merge_sorted_revisions_cache = None
95
97
        self._open_hook()
96
98
        hooks = Branch.hooks['open']
102
104
 
103
105
    def _activate_fallback_location(self, url):
104
106
        """Activate the branch/repository from url as a fallback repository."""
 
107
        for existing_fallback_repo in self.repository._fallback_repositories:
 
108
            if existing_fallback_repo.user_url == url:
 
109
                # This fallback is already configured.  This probably only
 
110
                # happens because BzrDir.sprout is a horrible mess.  To avoid
 
111
                # confusing _unstack we don't add this a second time.
 
112
                mutter('duplicate activation of fallback %r on %r', url, self)
 
113
                return
105
114
        repo = self._get_fallback_repository(url)
106
115
        if repo.has_same_location(self.repository):
107
116
            raise errors.UnstackableLocationError(self.user_url, url)
197
206
        return self.supports_tags() and self.tags.get_tag_dict()
198
207
 
199
208
    def get_config(self):
200
 
        return BranchConfig(self)
 
209
        """Get a bzrlib.config.BranchConfig for this Branch.
 
210
 
 
211
        This can then be used to get and set configuration options for the
 
212
        branch.
 
213
 
 
214
        :return: A bzrlib.config.BranchConfig.
 
215
        """
 
216
        return _mod_config.BranchConfig(self)
 
217
 
 
218
    def get_config_stack(self):
 
219
        """Get a bzrlib.config.BranchStack for this Branch.
 
220
 
 
221
        This can then be used to get and set configuration options for the
 
222
        branch.
 
223
 
 
224
        :return: A bzrlib.config.BranchStack.
 
225
        """
 
226
        return _mod_config.BranchStack(self)
201
227
 
202
228
    def _get_config(self):
203
229
        """Get the concrete config for just the config in this branch.
218
244
            possible_transports=[self.bzrdir.root_transport])
219
245
        return a_branch.repository
220
246
 
 
247
    @needs_read_lock
221
248
    def _get_tags_bytes(self):
222
249
        """Get the bytes of a serialised tags dict.
223
250
 
230
257
        :return: The bytes of the tags file.
231
258
        :seealso: Branch._set_tags_bytes.
232
259
        """
233
 
        return self._transport.get_bytes('tags')
 
260
        if self._tags_bytes is None:
 
261
            self._tags_bytes = self._transport.get_bytes('tags')
 
262
        return self._tags_bytes
234
263
 
235
264
    def _get_nick(self, local=False, possible_transports=None):
236
265
        config = self.get_config()
238
267
        if not local and not config.has_explicit_nickname():
239
268
            try:
240
269
                master = self.get_master_branch(possible_transports)
 
270
                if master and self.user_url == master.user_url:
 
271
                    raise errors.RecursiveBind(self.user_url)
241
272
                if master is not None:
242
273
                    # return the master branch value
243
274
                    return master.nick
 
275
            except errors.RecursiveBind, e:
 
276
                raise e
244
277
            except errors.BzrError, e:
245
278
                # Silently fall back to local implicit nick if the master is
246
279
                # unavailable
423
456
            after. If None, the rest of history is included.
424
457
        :param stop_rule: if stop_revision_id is not None, the precise rule
425
458
            to use for termination:
 
459
 
426
460
            * 'exclude' - leave the stop revision out of the result (default)
427
461
            * 'include' - the stop revision is the last item in the result
428
462
            * 'with-merges' - include the stop revision and all of its
430
464
            * 'with-merges-without-common-ancestry' - filter out revisions 
431
465
              that are in both ancestries
432
466
        :param direction: either 'reverse' or 'forward':
 
467
 
433
468
            * reverse means return the start_revision_id first, i.e.
434
469
              start at the most recent revision and go backwards in history
435
470
            * forward returns tuples in the opposite order to reverse.
479
514
        rev_iter = iter(merge_sorted_revisions)
480
515
        if start_revision_id is not None:
481
516
            for node in rev_iter:
482
 
                rev_id = node.key[-1]
 
517
                rev_id = node.key
483
518
                if rev_id != start_revision_id:
484
519
                    continue
485
520
                else:
486
521
                    # The decision to include the start or not
487
522
                    # depends on the stop_rule if a stop is provided
488
523
                    # so pop this node back into the iterator
489
 
                    rev_iter = chain(iter([node]), rev_iter)
 
524
                    rev_iter = itertools.chain(iter([node]), rev_iter)
490
525
                    break
491
526
        if stop_revision_id is None:
492
527
            # Yield everything
493
528
            for node in rev_iter:
494
 
                rev_id = node.key[-1]
 
529
                rev_id = node.key
495
530
                yield (rev_id, node.merge_depth, node.revno,
496
531
                       node.end_of_merge)
497
532
        elif stop_rule == 'exclude':
498
533
            for node in rev_iter:
499
 
                rev_id = node.key[-1]
 
534
                rev_id = node.key
500
535
                if rev_id == stop_revision_id:
501
536
                    return
502
537
                yield (rev_id, node.merge_depth, node.revno,
503
538
                       node.end_of_merge)
504
539
        elif stop_rule == 'include':
505
540
            for node in rev_iter:
506
 
                rev_id = node.key[-1]
 
541
                rev_id = node.key
507
542
                yield (rev_id, node.merge_depth, node.revno,
508
543
                       node.end_of_merge)
509
544
                if rev_id == stop_revision_id:
515
550
            ancestors = graph.find_unique_ancestors(start_revision_id,
516
551
                                                    [stop_revision_id])
517
552
            for node in rev_iter:
518
 
                rev_id = node.key[-1]
 
553
                rev_id = node.key
519
554
                if rev_id not in ancestors:
520
555
                    continue
521
556
                yield (rev_id, node.merge_depth, node.revno,
531
566
            reached_stop_revision_id = False
532
567
            revision_id_whitelist = []
533
568
            for node in rev_iter:
534
 
                rev_id = node.key[-1]
 
569
                rev_id = node.key
535
570
                if rev_id == left_parent:
536
571
                    # reached the left parent after the stop_revision
537
572
                    return
617
652
        """
618
653
        raise errors.UpgradeRequired(self.user_url)
619
654
 
 
655
    def get_append_revisions_only(self):
 
656
        """Whether it is only possible to append revisions to the history.
 
657
        """
 
658
        if not self._format.supports_set_append_revisions_only():
 
659
            return False
 
660
        return self.get_config(
 
661
            ).get_user_option_as_bool('append_revisions_only')
 
662
 
620
663
    def set_append_revisions_only(self, enabled):
621
664
        if not self._format.supports_set_append_revisions_only():
622
665
            raise errors.UpgradeRequired(self.user_url)
636
679
        raise errors.UnsupportedOperation(self.get_reference_info, self)
637
680
 
638
681
    @needs_write_lock
639
 
    def fetch(self, from_branch, last_revision=None, pb=None):
 
682
    def fetch(self, from_branch, last_revision=None, limit=None):
640
683
        """Copy revisions from from_branch into this branch.
641
684
 
642
685
        :param from_branch: Where to copy from.
643
686
        :param last_revision: What revision to stop at (None for at the end
644
687
                              of the branch.
645
 
        :param pb: An optional progress bar to use.
 
688
        :param limit: Optional rough limit of revisions to fetch
646
689
        :return: None
647
690
        """
648
 
        if self.base == from_branch.base:
649
 
            return (0, [])
650
 
        if pb is not None:
651
 
            symbol_versioning.warn(
652
 
                symbol_versioning.deprecated_in((1, 14, 0))
653
 
                % "pb parameter to fetch()")
654
 
        from_branch.lock_read()
655
 
        try:
656
 
            if last_revision is None:
657
 
                last_revision = from_branch.last_revision()
658
 
                last_revision = _mod_revision.ensure_null(last_revision)
659
 
            return self.repository.fetch(from_branch.repository,
660
 
                                         revision_id=last_revision,
661
 
                                         pb=pb)
662
 
        finally:
663
 
            from_branch.unlock()
 
691
        return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
664
692
 
665
693
    def get_bound_location(self):
666
694
        """Return the URL of the branch we are bound to.
677
705
 
678
706
    def get_commit_builder(self, parents, config=None, timestamp=None,
679
707
                           timezone=None, committer=None, revprops=None,
680
 
                           revision_id=None):
 
708
                           revision_id=None, lossy=False):
681
709
        """Obtain a CommitBuilder for this branch.
682
710
 
683
711
        :param parents: Revision ids of the parents of the new revision.
687
715
        :param committer: Optional committer to set for commit.
688
716
        :param revprops: Optional dictionary of revision properties.
689
717
        :param revision_id: Optional revision id.
 
718
        :param lossy: Whether to discard data that can not be natively
 
719
            represented, when pushing to a foreign VCS 
690
720
        """
691
721
 
692
722
        if config is None:
693
723
            config = self.get_config()
694
724
 
695
725
        return self.repository.get_commit_builder(self, parents, config,
696
 
            timestamp, timezone, committer, revprops, revision_id)
 
726
            timestamp, timezone, committer, revprops, revision_id,
 
727
            lossy)
697
728
 
698
729
    def get_master_branch(self, possible_transports=None):
699
730
        """Return the branch we are bound to.
726
757
        """Print `file` to stdout."""
727
758
        raise NotImplementedError(self.print_file)
728
759
 
 
760
    @deprecated_method(deprecated_in((2, 4, 0)))
729
761
    def set_revision_history(self, rev_history):
730
 
        raise NotImplementedError(self.set_revision_history)
 
762
        """See Branch.set_revision_history."""
 
763
        self._set_revision_history(rev_history)
 
764
 
 
765
    @needs_write_lock
 
766
    def _set_revision_history(self, rev_history):
 
767
        if len(rev_history) == 0:
 
768
            revid = _mod_revision.NULL_REVISION
 
769
        else:
 
770
            revid = rev_history[-1]
 
771
        if rev_history != self._lefthand_history(revid):
 
772
            raise errors.NotLefthandHistory(rev_history)
 
773
        self.set_last_revision_info(len(rev_history), revid)
 
774
        self._cache_revision_history(rev_history)
 
775
        for hook in Branch.hooks['set_rh']:
 
776
            hook(self, rev_history)
 
777
 
 
778
    @needs_write_lock
 
779
    def set_last_revision_info(self, revno, revision_id):
 
780
        """Set the last revision of this branch.
 
781
 
 
782
        The caller is responsible for checking that the revno is correct
 
783
        for this revision id.
 
784
 
 
785
        It may be possible to set the branch last revision to an id not
 
786
        present in the repository.  However, branches can also be
 
787
        configured to check constraints on history, in which case this may not
 
788
        be permitted.
 
789
        """
 
790
        raise NotImplementedError(self.set_last_revision_info)
 
791
 
 
792
    @needs_write_lock
 
793
    def generate_revision_history(self, revision_id, last_rev=None,
 
794
                                  other_branch=None):
 
795
        """See Branch.generate_revision_history"""
 
796
        graph = self.repository.get_graph()
 
797
        (last_revno, last_revid) = self.last_revision_info()
 
798
        known_revision_ids = [
 
799
            (last_revid, last_revno),
 
800
            (_mod_revision.NULL_REVISION, 0),
 
801
            ]
 
802
        if last_rev is not None:
 
803
            if not graph.is_ancestor(last_rev, revision_id):
 
804
                # our previous tip is not merged into stop_revision
 
805
                raise errors.DivergedBranches(self, other_branch)
 
806
        revno = graph.find_distance_to_null(revision_id, known_revision_ids)
 
807
        self.set_last_revision_info(revno, revision_id)
731
808
 
732
809
    @needs_write_lock
733
810
    def set_parent(self, url):
777
854
 
778
855
    def _unstack(self):
779
856
        """Change a branch to be unstacked, copying data as needed.
780
 
        
 
857
 
781
858
        Don't call this directly, use set_stacked_on_url(None).
782
859
        """
783
860
        pb = ui.ui_factory.nested_progress_bar()
784
861
        try:
785
 
            pb.update("Unstacking")
 
862
            pb.update(gettext("Unstacking"))
786
863
            # The basic approach here is to fetch the tip of the branch,
787
864
            # including all available ghosts, from the existing stacked
788
865
            # repository into a new repository object without the fallbacks. 
792
869
            old_repository = self.repository
793
870
            if len(old_repository._fallback_repositories) != 1:
794
871
                raise AssertionError("can't cope with fallback repositories "
795
 
                    "of %r" % (self.repository,))
796
 
            # unlock it, including unlocking the fallback
 
872
                    "of %r (fallbacks: %r)" % (old_repository,
 
873
                        old_repository._fallback_repositories))
 
874
            # Open the new repository object.
 
875
            # Repositories don't offer an interface to remove fallback
 
876
            # repositories today; take the conceptually simpler option and just
 
877
            # reopen it.  We reopen it starting from the URL so that we
 
878
            # get a separate connection for RemoteRepositories and can
 
879
            # stream from one of them to the other.  This does mean doing
 
880
            # separate SSH connection setup, but unstacking is not a
 
881
            # common operation so it's tolerable.
 
882
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
 
883
            new_repository = new_bzrdir.find_repository()
 
884
            if new_repository._fallback_repositories:
 
885
                raise AssertionError("didn't expect %r to have "
 
886
                    "fallback_repositories"
 
887
                    % (self.repository,))
 
888
            # Replace self.repository with the new repository.
 
889
            # Do our best to transfer the lock state (i.e. lock-tokens and
 
890
            # lock count) of self.repository to the new repository.
 
891
            lock_token = old_repository.lock_write().repository_token
 
892
            self.repository = new_repository
 
893
            if isinstance(self, remote.RemoteBranch):
 
894
                # Remote branches can have a second reference to the old
 
895
                # repository that need to be replaced.
 
896
                if self._real_branch is not None:
 
897
                    self._real_branch.repository = new_repository
 
898
            self.repository.lock_write(token=lock_token)
 
899
            if lock_token is not None:
 
900
                old_repository.leave_lock_in_place()
797
901
            old_repository.unlock()
 
902
            if lock_token is not None:
 
903
                # XXX: self.repository.leave_lock_in_place() before this
 
904
                # function will not be preserved.  Fortunately that doesn't
 
905
                # affect the current default format (2a), and would be a
 
906
                # corner-case anyway.
 
907
                #  - Andrew Bennetts, 2010/06/30
 
908
                self.repository.dont_leave_lock_in_place()
 
909
            old_lock_count = 0
 
910
            while True:
 
911
                try:
 
912
                    old_repository.unlock()
 
913
                except errors.LockNotHeld:
 
914
                    break
 
915
                old_lock_count += 1
 
916
            if old_lock_count == 0:
 
917
                raise AssertionError(
 
918
                    'old_repository should have been locked at least once.')
 
919
            for i in range(old_lock_count-1):
 
920
                self.repository.lock_write()
 
921
            # Fetch from the old repository into the new.
798
922
            old_repository.lock_read()
799
923
            try:
800
 
                # Repositories don't offer an interface to remove fallback
801
 
                # repositories today; take the conceptually simpler option and just
802
 
                # reopen it.  We reopen it starting from the URL so that we
803
 
                # get a separate connection for RemoteRepositories and can
804
 
                # stream from one of them to the other.  This does mean doing
805
 
                # separate SSH connection setup, but unstacking is not a
806
 
                # common operation so it's tolerable.
807
 
                new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
808
 
                new_repository = new_bzrdir.find_repository()
809
 
                self.repository = new_repository
810
 
                if self.repository._fallback_repositories:
811
 
                    raise AssertionError("didn't expect %r to have "
812
 
                        "fallback_repositories"
813
 
                        % (self.repository,))
814
 
                # this is not paired with an unlock because it's just restoring
815
 
                # the previous state; the lock's released when set_stacked_on_url
816
 
                # returns
817
 
                self.repository.lock_write()
818
924
                # XXX: If you unstack a branch while it has a working tree
819
925
                # with a pending merge, the pending-merged revisions will no
820
926
                # longer be present.  You can (probably) revert and remerge.
821
 
                #
822
 
                # XXX: This only fetches up to the tip of the repository; it
823
 
                # doesn't bring across any tags.  That's fairly consistent
824
 
                # with how branch works, but perhaps not ideal.
825
 
                self.repository.fetch(old_repository,
826
 
                    revision_id=self.last_revision(),
827
 
                    find_ghosts=True)
 
927
                try:
 
928
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
 
929
                except errors.TagsNotSupported:
 
930
                    tags_to_fetch = set()
 
931
                fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
 
932
                    old_repository, required_ids=[self.last_revision()],
 
933
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
 
934
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
828
935
            finally:
829
936
                old_repository.unlock()
830
937
        finally:
835
942
 
836
943
        :seealso: Branch._get_tags_bytes.
837
944
        """
838
 
        return _run_with_write_locked_target(self, self._transport.put_bytes,
839
 
            'tags', bytes)
 
945
        op = cleanup.OperationWithCleanups(self._set_tags_bytes_locked)
 
946
        op.add_cleanup(self.lock_write().unlock)
 
947
        return op.run_simple(bytes)
 
948
 
 
949
    def _set_tags_bytes_locked(self, bytes):
 
950
        self._tags_bytes = bytes
 
951
        return self._transport.put_bytes('tags', bytes)
840
952
 
841
953
    def _cache_revision_history(self, rev_history):
842
954
        """Set the cached revision history to rev_history.
869
981
        self._revision_history_cache = None
870
982
        self._revision_id_to_revno_cache = None
871
983
        self._last_revision_info_cache = None
 
984
        self._master_branch_cache = None
872
985
        self._merge_sorted_revisions_cache = None
873
986
        self._partial_revision_history_cache = []
874
987
        self._partial_revision_id_to_revno_cache = {}
 
988
        self._tags_bytes = None
875
989
 
876
990
    def _gen_revision_history(self):
877
991
        """Return sequence of revision hashes on to this branch.
927
1041
        :return: A tuple (revno, revision_id).
928
1042
        """
929
1043
        if self._last_revision_info_cache is None:
930
 
            self._last_revision_info_cache = self._last_revision_info()
 
1044
            self._last_revision_info_cache = self._read_last_revision_info()
931
1045
        return self._last_revision_info_cache
932
1046
 
933
 
    def _last_revision_info(self):
934
 
        rh = self.revision_history()
935
 
        revno = len(rh)
936
 
        if revno:
937
 
            return (revno, rh[-1])
938
 
        else:
939
 
            return (0, _mod_revision.NULL_REVISION)
940
 
 
941
 
    @deprecated_method(deprecated_in((1, 6, 0)))
942
 
    def missing_revisions(self, other, stop_revision=None):
943
 
        """Return a list of new revisions that would perfectly fit.
944
 
 
945
 
        If self and other have not diverged, return a list of the revisions
946
 
        present in other, but missing from self.
947
 
        """
948
 
        self_history = self.revision_history()
949
 
        self_len = len(self_history)
950
 
        other_history = other.revision_history()
951
 
        other_len = len(other_history)
952
 
        common_index = min(self_len, other_len) -1
953
 
        if common_index >= 0 and \
954
 
            self_history[common_index] != other_history[common_index]:
955
 
            raise errors.DivergedBranches(self, other)
956
 
 
957
 
        if stop_revision is None:
958
 
            stop_revision = other_len
959
 
        else:
960
 
            if stop_revision > other_len:
961
 
                raise errors.NoSuchRevision(self, stop_revision)
962
 
        return other_history[self_len:stop_revision]
963
 
 
964
 
    @needs_write_lock
965
 
    def update_revisions(self, other, stop_revision=None, overwrite=False,
966
 
                         graph=None):
967
 
        """Pull in new perfect-fit revisions.
968
 
 
969
 
        :param other: Another Branch to pull from
970
 
        :param stop_revision: Updated until the given revision
971
 
        :param overwrite: Always set the branch pointer, rather than checking
972
 
            to see if it is a proper descendant.
973
 
        :param graph: A Graph object that can be used to query history
974
 
            information. This can be None.
975
 
        :return: None
976
 
        """
977
 
        return InterBranch.get(other, self).update_revisions(stop_revision,
978
 
            overwrite, graph)
979
 
 
 
1047
    def _read_last_revision_info(self):
 
1048
        raise NotImplementedError(self._read_last_revision_info)
 
1049
 
 
1050
    @deprecated_method(deprecated_in((2, 4, 0)))
980
1051
    def import_last_revision_info(self, source_repo, revno, revid):
981
1052
        """Set the last revision info, importing from another repo if necessary.
982
1053
 
983
 
        This is used by the bound branch code to upload a revision to
984
 
        the master branch first before updating the tip of the local branch.
985
 
 
986
1054
        :param source_repo: Source repository to optionally fetch from
987
1055
        :param revno: Revision number of the new tip
988
1056
        :param revid: Revision id of the new tip
991
1059
            self.repository.fetch(source_repo, revision_id=revid)
992
1060
        self.set_last_revision_info(revno, revid)
993
1061
 
 
1062
    def import_last_revision_info_and_tags(self, source, revno, revid,
 
1063
                                           lossy=False):
 
1064
        """Set the last revision info, importing from another repo if necessary.
 
1065
 
 
1066
        This is used by the bound branch code to upload a revision to
 
1067
        the master branch first before updating the tip of the local branch.
 
1068
        Revisions referenced by source's tags are also transferred.
 
1069
 
 
1070
        :param source: Source branch to optionally fetch from
 
1071
        :param revno: Revision number of the new tip
 
1072
        :param revid: Revision id of the new tip
 
1073
        :param lossy: Whether to discard metadata that can not be
 
1074
            natively represented
 
1075
        :return: Tuple with the new revision number and revision id
 
1076
            (should only be different from the arguments when lossy=True)
 
1077
        """
 
1078
        if not self.repository.has_same_location(source.repository):
 
1079
            self.fetch(source, revid)
 
1080
        self.set_last_revision_info(revno, revid)
 
1081
        return (revno, revid)
 
1082
 
994
1083
    def revision_id_to_revno(self, revision_id):
995
1084
        """Given a revision id, return its revno"""
996
1085
        if _mod_revision.is_null(revision_id):
1016
1105
            self._extend_partial_history(distance_from_last)
1017
1106
        return self._partial_revision_history_cache[distance_from_last]
1018
1107
 
1019
 
    @needs_write_lock
1020
1108
    def pull(self, source, overwrite=False, stop_revision=None,
1021
1109
             possible_transports=None, *args, **kwargs):
1022
1110
        """Mirror source into this branch.
1029
1117
            stop_revision=stop_revision,
1030
1118
            possible_transports=possible_transports, *args, **kwargs)
1031
1119
 
1032
 
    def push(self, target, overwrite=False, stop_revision=None, *args,
1033
 
        **kwargs):
 
1120
    def push(self, target, overwrite=False, stop_revision=None, lossy=False,
 
1121
            *args, **kwargs):
1034
1122
        """Mirror this branch into target.
1035
1123
 
1036
1124
        This branch is considered to be 'local', having low latency.
1037
1125
        """
1038
1126
        return InterBranch.get(self, target).push(overwrite, stop_revision,
1039
 
            *args, **kwargs)
1040
 
 
1041
 
    def lossy_push(self, target, stop_revision=None):
1042
 
        """Push deltas into another branch.
1043
 
 
1044
 
        :note: This does not, like push, retain the revision ids from 
1045
 
            the source branch and will, rather than adding bzr-specific 
1046
 
            metadata, push only those semantics of the revision that can be 
1047
 
            natively represented by this branch' VCS.
1048
 
 
1049
 
        :param target: Target branch
1050
 
        :param stop_revision: Revision to push, defaults to last revision.
1051
 
        :return: BranchPushResult with an extra member revidmap: 
1052
 
            A dictionary mapping revision ids from the target branch 
1053
 
            to new revision ids in the target branch, for each 
1054
 
            revision that was pushed.
1055
 
        """
1056
 
        inter = InterBranch.get(self, target)
1057
 
        lossy_push = getattr(inter, "lossy_push", None)
1058
 
        if lossy_push is None:
1059
 
            raise errors.LossyPushToSameVCS(self, target)
1060
 
        return lossy_push(stop_revision)
 
1127
            lossy, *args, **kwargs)
1061
1128
 
1062
1129
    def basis_tree(self):
1063
1130
        """Return `Tree` object for last revision."""
1218
1285
        return result
1219
1286
 
1220
1287
    @needs_read_lock
1221
 
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
 
1288
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
 
1289
            repository=None):
1222
1290
        """Create a new line of development from the branch, into to_bzrdir.
1223
1291
 
1224
1292
        to_bzrdir controls the branch format.
1229
1297
        if (repository_policy is not None and
1230
1298
            repository_policy.requires_stacking()):
1231
1299
            to_bzrdir._format.require_stacking(_skip_repo=True)
1232
 
        result = to_bzrdir.create_branch()
 
1300
        result = to_bzrdir.create_branch(repository=repository)
1233
1301
        result.lock_write()
1234
1302
        try:
1235
1303
            if repository_policy is not None:
1236
1304
                repository_policy.configure_branch(result)
1237
1305
            self.copy_content_into(result, revision_id=revision_id)
1238
 
            result.set_parent(self.bzrdir.root_transport.base)
 
1306
            master_url = self.get_bound_location()
 
1307
            if master_url is None:
 
1308
                result.set_parent(self.bzrdir.root_transport.base)
 
1309
            else:
 
1310
                result.set_parent(master_url)
1239
1311
        finally:
1240
1312
            result.unlock()
1241
1313
        return result
1265
1337
                revno = 1
1266
1338
        destination.set_last_revision_info(revno, revision_id)
1267
1339
 
1268
 
    @needs_read_lock
1269
1340
    def copy_content_into(self, destination, revision_id=None):
1270
1341
        """Copy the content of self into destination.
1271
1342
 
1272
1343
        revision_id: if not None, the revision history in the new branch will
1273
1344
                     be truncated to end with revision_id.
1274
1345
        """
1275
 
        self.update_references(destination)
1276
 
        self._synchronize_history(destination, revision_id)
1277
 
        try:
1278
 
            parent = self.get_parent()
1279
 
        except errors.InaccessibleParent, e:
1280
 
            mutter('parent was not accessible to copy: %s', e)
1281
 
        else:
1282
 
            if parent:
1283
 
                destination.set_parent(parent)
1284
 
        if self._push_should_merge_tags():
1285
 
            self.tags.merge_to(destination.tags)
 
1346
        return InterBranch.get(self, destination).copy_content_into(
 
1347
            revision_id=revision_id)
1286
1348
 
1287
1349
    def update_references(self, target):
1288
1350
        if not getattr(self._format, 'supports_reference_locations', False):
1329
1391
        # specific check.
1330
1392
        return result
1331
1393
 
1332
 
    def _get_checkout_format(self):
 
1394
    def _get_checkout_format(self, lightweight=False):
1333
1395
        """Return the most suitable metadir for a checkout of this branch.
1334
1396
        Weaves are used if this branch's repository uses weaves.
1335
1397
        """
1336
 
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
1337
 
            from bzrlib.repofmt import weaverepo
1338
 
            format = bzrdir.BzrDirMetaFormat1()
1339
 
            format.repository_format = weaverepo.RepositoryFormat7()
1340
 
        else:
1341
 
            format = self.repository.bzrdir.checkout_metadir()
1342
 
            format.set_branch_format(self._format)
 
1398
        format = self.repository.bzrdir.checkout_metadir()
 
1399
        format.set_branch_format(self._format)
1343
1400
        return format
1344
1401
 
1345
1402
    def create_clone_on_transport(self, to_transport, revision_id=None,
1346
 
        stacked_on=None, create_prefix=False, use_existing_dir=False):
 
1403
        stacked_on=None, create_prefix=False, use_existing_dir=False,
 
1404
        no_tree=None):
1347
1405
        """Create a clone of this branch and its bzrdir.
1348
1406
 
1349
1407
        :param to_transport: The transport to clone onto.
1362
1420
            revision_id = self.last_revision()
1363
1421
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1364
1422
            revision_id=revision_id, stacked_on=stacked_on,
1365
 
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
 
1423
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
 
1424
            no_tree=no_tree)
1366
1425
        return dir_to.open_branch()
1367
1426
 
1368
1427
    def create_checkout(self, to_location, revision_id=None,
1373
1432
        :param to_location: The url to produce the checkout at
1374
1433
        :param revision_id: The revision to check out
1375
1434
        :param lightweight: If True, produce a lightweight checkout, otherwise,
1376
 
        produce a bound branch (heavyweight checkout)
 
1435
            produce a bound branch (heavyweight checkout)
1377
1436
        :param accelerator_tree: A tree which can be used for retrieving file
1378
1437
            contents more quickly than the revision tree, i.e. a workingtree.
1379
1438
            The revision tree will be used for cases where accelerator_tree's
1384
1443
        """
1385
1444
        t = transport.get_transport(to_location)
1386
1445
        t.ensure_base()
 
1446
        format = self._get_checkout_format(lightweight=lightweight)
1387
1447
        if lightweight:
1388
 
            format = self._get_checkout_format()
1389
1448
            checkout = format.initialize_on_transport(t)
1390
1449
            from_branch = BranchReferenceFormat().initialize(checkout, 
1391
1450
                target_branch=self)
1392
1451
        else:
1393
 
            format = self._get_checkout_format()
1394
1452
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1395
1453
                to_location, force_new_tree=False, format=format)
1396
1454
            checkout = checkout_branch.bzrdir
1425
1483
 
1426
1484
    def reference_parent(self, file_id, path, possible_transports=None):
1427
1485
        """Return the parent branch for a tree-reference file_id
 
1486
 
1428
1487
        :param file_id: The file_id of the tree reference
1429
1488
        :param path: The path of the file_id in the tree
1430
1489
        :return: A branch associated with the file_id
1483
1542
        else:
1484
1543
            raise AssertionError("invalid heads: %r" % (heads,))
1485
1544
 
1486
 
 
1487
 
class BranchFormat(object):
 
1545
    def heads_to_fetch(self):
 
1546
        """Return the heads that must and that should be fetched to copy this
 
1547
        branch into another repo.
 
1548
 
 
1549
        :returns: a 2-tuple of (must_fetch, if_present_fetch).  must_fetch is a
 
1550
            set of heads that must be fetched.  if_present_fetch is a set of
 
1551
            heads that must be fetched if present, but no error is necessary if
 
1552
            they are not present.
 
1553
        """
 
1554
        # For bzr native formats must_fetch is just the tip, and if_present_fetch
 
1555
        # are the tags.
 
1556
        must_fetch = set([self.last_revision()])
 
1557
        if_present_fetch = set()
 
1558
        c = self.get_config()
 
1559
        include_tags = c.get_user_option_as_bool('branch.fetch_tags',
 
1560
                                                 default=False)
 
1561
        if include_tags:
 
1562
            try:
 
1563
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
 
1564
            except errors.TagsNotSupported:
 
1565
                pass
 
1566
        must_fetch.discard(_mod_revision.NULL_REVISION)
 
1567
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
 
1568
        return must_fetch, if_present_fetch
 
1569
 
 
1570
 
 
1571
class BranchFormat(controldir.ControlComponentFormat):
1488
1572
    """An encapsulation of the initialization and open routines for a format.
1489
1573
 
1490
1574
    Formats provide three things:
1493
1577
     * an open routine.
1494
1578
 
1495
1579
    Formats are placed in an dict by their format string for reference
1496
 
    during branch opening. Its not required that these be instances, they
 
1580
    during branch opening. It's not required that these be instances, they
1497
1581
    can be classes themselves with class methods - it simply depends on
1498
1582
    whether state is needed for a given format or not.
1499
1583
 
1502
1586
    object will be created every time regardless.
1503
1587
    """
1504
1588
 
1505
 
    _default_format = None
1506
 
    """The default format used for new branches."""
1507
 
 
1508
 
    _formats = {}
1509
 
    """The known formats."""
1510
 
 
1511
 
    can_set_append_revisions_only = True
1512
 
 
1513
1589
    def __eq__(self, other):
1514
1590
        return self.__class__ is other.__class__
1515
1591
 
1522
1598
        try:
1523
1599
            transport = a_bzrdir.get_branch_transport(None, name=name)
1524
1600
            format_string = transport.get_bytes("format")
1525
 
            return klass._formats[format_string]
 
1601
            return format_registry.get(format_string)
1526
1602
        except errors.NoSuchFile:
1527
1603
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1528
1604
        except KeyError:
1529
1605
            raise errors.UnknownFormatError(format=format_string, kind='branch')
1530
1606
 
1531
1607
    @classmethod
 
1608
    @deprecated_method(deprecated_in((2, 4, 0)))
1532
1609
    def get_default_format(klass):
1533
1610
        """Return the current default format."""
1534
 
        return klass._default_format
 
1611
        return format_registry.get_default()
 
1612
 
 
1613
    @classmethod
 
1614
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1615
    def get_formats(klass):
 
1616
        """Get all the known formats.
 
1617
 
 
1618
        Warning: This triggers a load of all lazy registered formats: do not
 
1619
        use except when that is desireed.
 
1620
        """
 
1621
        return format_registry._get_all()
1535
1622
 
1536
1623
    def get_reference(self, a_bzrdir, name=None):
1537
1624
        """Get the target reference of the branch in a_bzrdir.
1576
1663
        for hook in hooks:
1577
1664
            hook(params)
1578
1665
 
1579
 
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1580
 
                           lock_type='metadir', set_format=True):
1581
 
        """Initialize a branch in a bzrdir, with specified files
1582
 
 
1583
 
        :param a_bzrdir: The bzrdir to initialize the branch in
1584
 
        :param utf8_files: The files to create as a list of
1585
 
            (filename, content) tuples
1586
 
        :param name: Name of colocated branch to create, if any
1587
 
        :param set_format: If True, set the format with
1588
 
            self.get_format_string.  (BzrBranch4 has its format set
1589
 
            elsewhere)
1590
 
        :return: a branch in this format
1591
 
        """
1592
 
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1593
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1594
 
        lock_map = {
1595
 
            'metadir': ('lock', lockdir.LockDir),
1596
 
            'branch4': ('branch-lock', lockable_files.TransportLock),
1597
 
        }
1598
 
        lock_name, lock_class = lock_map[lock_type]
1599
 
        control_files = lockable_files.LockableFiles(branch_transport,
1600
 
            lock_name, lock_class)
1601
 
        control_files.create_lock()
1602
 
        try:
1603
 
            control_files.lock_write()
1604
 
        except errors.LockContention:
1605
 
            if lock_type != 'branch4':
1606
 
                raise
1607
 
            lock_taken = False
1608
 
        else:
1609
 
            lock_taken = True
1610
 
        if set_format:
1611
 
            utf8_files += [('format', self.get_format_string())]
1612
 
        try:
1613
 
            for (filename, content) in utf8_files:
1614
 
                branch_transport.put_bytes(
1615
 
                    filename, content,
1616
 
                    mode=a_bzrdir._get_file_mode())
1617
 
        finally:
1618
 
            if lock_taken:
1619
 
                control_files.unlock()
1620
 
        branch = self.open(a_bzrdir, name, _found=True)
1621
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1622
 
        return branch
1623
 
 
1624
 
    def initialize(self, a_bzrdir, name=None):
 
1666
    def initialize(self, a_bzrdir, name=None, repository=None,
 
1667
                   append_revisions_only=None):
1625
1668
        """Create a branch of this format in a_bzrdir.
1626
1669
        
1627
1670
        :param name: Name of the colocated branch to create.
1649
1692
        Note that it is normal for branch to be a RemoteBranch when using tags
1650
1693
        on a RemoteBranch.
1651
1694
        """
1652
 
        return DisabledTags(branch)
 
1695
        return _mod_tag.DisabledTags(branch)
1653
1696
 
1654
1697
    def network_name(self):
1655
1698
        """A simple byte string uniquely identifying this format for RPC calls.
1661
1704
        """
1662
1705
        raise NotImplementedError(self.network_name)
1663
1706
 
1664
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
1707
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
 
1708
            found_repository=None):
1665
1709
        """Return the branch object for a_bzrdir
1666
1710
 
1667
1711
        :param a_bzrdir: A BzrDir that contains a branch.
1674
1718
        raise NotImplementedError(self.open)
1675
1719
 
1676
1720
    @classmethod
 
1721
    @deprecated_method(deprecated_in((2, 4, 0)))
1677
1722
    def register_format(klass, format):
1678
 
        """Register a metadir format."""
1679
 
        klass._formats[format.get_format_string()] = format
1680
 
        # Metadir formats have a network name of their format string, and get
1681
 
        # registered as class factories.
1682
 
        network_format_registry.register(format.get_format_string(), format.__class__)
 
1723
        """Register a metadir format.
 
1724
 
 
1725
        See MetaDirBranchFormatFactory for the ability to register a format
 
1726
        without loading the code the format needs until it is actually used.
 
1727
        """
 
1728
        format_registry.register(format)
1683
1729
 
1684
1730
    @classmethod
 
1731
    @deprecated_method(deprecated_in((2, 4, 0)))
1685
1732
    def set_default_format(klass, format):
1686
 
        klass._default_format = format
 
1733
        format_registry.set_default(format)
1687
1734
 
1688
1735
    def supports_set_append_revisions_only(self):
1689
1736
        """True if this format supports set_append_revisions_only."""
1693
1740
        """True if this format records a stacked-on branch."""
1694
1741
        return False
1695
1742
 
 
1743
    def supports_leaving_lock(self):
 
1744
        """True if this format supports leaving locks in place."""
 
1745
        return False # by default
 
1746
 
1696
1747
    @classmethod
 
1748
    @deprecated_method(deprecated_in((2, 4, 0)))
1697
1749
    def unregister_format(klass, format):
1698
 
        del klass._formats[format.get_format_string()]
 
1750
        format_registry.remove(format)
1699
1751
 
1700
1752
    def __str__(self):
1701
1753
        return self.get_format_description().rstrip()
1704
1756
        """True if this format supports tags stored in the branch"""
1705
1757
        return False  # by default
1706
1758
 
 
1759
    def tags_are_versioned(self):
 
1760
        """Whether the tag container for this branch versions tags."""
 
1761
        return False
 
1762
 
 
1763
    def supports_tags_referencing_ghosts(self):
 
1764
        """True if tags can reference ghost revisions."""
 
1765
        return True
 
1766
 
 
1767
 
 
1768
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
 
1769
    """A factory for a BranchFormat object, permitting simple lazy registration.
 
1770
    
 
1771
    While none of the built in BranchFormats are lazy registered yet,
 
1772
    bzrlib.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
 
1773
    use it, and the bzr-loom plugin uses it as well (see
 
1774
    bzrlib.plugins.loom.formats).
 
1775
    """
 
1776
 
 
1777
    def __init__(self, format_string, module_name, member_name):
 
1778
        """Create a MetaDirBranchFormatFactory.
 
1779
 
 
1780
        :param format_string: The format string the format has.
 
1781
        :param module_name: Module to load the format class from.
 
1782
        :param member_name: Attribute name within the module for the format class.
 
1783
        """
 
1784
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
 
1785
        self._format_string = format_string
 
1786
        
 
1787
    def get_format_string(self):
 
1788
        """See BranchFormat.get_format_string."""
 
1789
        return self._format_string
 
1790
 
 
1791
    def __call__(self):
 
1792
        """Used for network_format_registry support."""
 
1793
        return self.get_obj()()
 
1794
 
1707
1795
 
1708
1796
class BranchHooks(Hooks):
1709
1797
    """A dictionary mapping hook name to a list of callables for branch hooks.
1718
1806
        These are all empty initially, because by default nothing should get
1719
1807
        notified.
1720
1808
        """
1721
 
        Hooks.__init__(self)
1722
 
        self.create_hook(HookPoint('set_rh',
 
1809
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
 
1810
        self.add_hook('set_rh',
1723
1811
            "Invoked whenever the revision history has been set via "
1724
1812
            "set_revision_history. The api signature is (branch, "
1725
1813
            "revision_history), and the branch will be write-locked. "
1726
1814
            "The set_rh hook can be expensive for bzr to trigger, a better "
1727
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1728
 
        self.create_hook(HookPoint('open',
 
1815
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
 
1816
        self.add_hook('open',
1729
1817
            "Called with the Branch object that has been opened after a "
1730
 
            "branch is opened.", (1, 8), None))
1731
 
        self.create_hook(HookPoint('post_push',
 
1818
            "branch is opened.", (1, 8))
 
1819
        self.add_hook('post_push',
1732
1820
            "Called after a push operation completes. post_push is called "
1733
1821
            "with a bzrlib.branch.BranchPushResult object and only runs in the "
1734
 
            "bzr client.", (0, 15), None))
1735
 
        self.create_hook(HookPoint('post_pull',
 
1822
            "bzr client.", (0, 15))
 
1823
        self.add_hook('post_pull',
1736
1824
            "Called after a pull operation completes. post_pull is called "
1737
1825
            "with a bzrlib.branch.PullResult object and only runs in the "
1738
 
            "bzr client.", (0, 15), None))
1739
 
        self.create_hook(HookPoint('pre_commit',
1740
 
            "Called after a commit is calculated but before it is is "
 
1826
            "bzr client.", (0, 15))
 
1827
        self.add_hook('pre_commit',
 
1828
            "Called after a commit is calculated but before it is "
1741
1829
            "completed. pre_commit is called with (local, master, old_revno, "
1742
1830
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1743
1831
            "). old_revid is NULL_REVISION for the first commit to a branch, "
1745
1833
            "basis revision. hooks MUST NOT modify this delta. "
1746
1834
            " future_tree is an in-memory tree obtained from "
1747
1835
            "CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1748
 
            "tree.", (0,91), None))
1749
 
        self.create_hook(HookPoint('post_commit',
 
1836
            "tree.", (0,91))
 
1837
        self.add_hook('post_commit',
1750
1838
            "Called in the bzr client after a commit has completed. "
1751
1839
            "post_commit is called with (local, master, old_revno, old_revid, "
1752
1840
            "new_revno, new_revid). old_revid is NULL_REVISION for the first "
1753
 
            "commit to a branch.", (0, 15), None))
1754
 
        self.create_hook(HookPoint('post_uncommit',
 
1841
            "commit to a branch.", (0, 15))
 
1842
        self.add_hook('post_uncommit',
1755
1843
            "Called in the bzr client after an uncommit completes. "
1756
1844
            "post_uncommit is called with (local, master, old_revno, "
1757
1845
            "old_revid, new_revno, new_revid) where local is the local branch "
1758
1846
            "or None, master is the target branch, and an empty branch "
1759
 
            "receives new_revno of 0, new_revid of None.", (0, 15), None))
1760
 
        self.create_hook(HookPoint('pre_change_branch_tip',
 
1847
            "receives new_revno of 0, new_revid of None.", (0, 15))
 
1848
        self.add_hook('pre_change_branch_tip',
1761
1849
            "Called in bzr client and server before a change to the tip of a "
1762
1850
            "branch is made. pre_change_branch_tip is called with a "
1763
1851
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1764
 
            "commit, uncommit will all trigger this hook.", (1, 6), None))
1765
 
        self.create_hook(HookPoint('post_change_branch_tip',
 
1852
            "commit, uncommit will all trigger this hook.", (1, 6))
 
1853
        self.add_hook('post_change_branch_tip',
1766
1854
            "Called in bzr client and server after a change to the tip of a "
1767
1855
            "branch is made. post_change_branch_tip is called with a "
1768
1856
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1769
 
            "commit, uncommit will all trigger this hook.", (1, 4), None))
1770
 
        self.create_hook(HookPoint('transform_fallback_location',
 
1857
            "commit, uncommit will all trigger this hook.", (1, 4))
 
1858
        self.add_hook('transform_fallback_location',
1771
1859
            "Called when a stacked branch is activating its fallback "
1772
1860
            "locations. transform_fallback_location is called with (branch, "
1773
1861
            "url), and should return a new url. Returning the same url "
1778
1866
            "fallback locations have not been activated. When there are "
1779
1867
            "multiple hooks installed for transform_fallback_location, "
1780
1868
            "all are called with the url returned from the previous hook."
1781
 
            "The order is however undefined.", (1, 9), None))
1782
 
        self.create_hook(HookPoint('automatic_tag_name',
1783
 
            "Called to determine an automatic tag name for a revision."
 
1869
            "The order is however undefined.", (1, 9))
 
1870
        self.add_hook('automatic_tag_name',
 
1871
            "Called to determine an automatic tag name for a revision. "
1784
1872
            "automatic_tag_name is called with (branch, revision_id) and "
1785
1873
            "should return a tag name or None if no tag name could be "
1786
1874
            "determined. The first non-None tag name returned will be used.",
1787
 
            (2, 2), None))
1788
 
        self.create_hook(HookPoint('post_branch_init',
 
1875
            (2, 2))
 
1876
        self.add_hook('post_branch_init',
1789
1877
            "Called after new branch initialization completes. "
1790
1878
            "post_branch_init is called with a "
1791
1879
            "bzrlib.branch.BranchInitHookParams. "
1792
1880
            "Note that init, branch and checkout (both heavyweight and "
1793
 
            "lightweight) will all trigger this hook.", (2, 2), None))
1794
 
        self.create_hook(HookPoint('post_switch',
 
1881
            "lightweight) will all trigger this hook.", (2, 2))
 
1882
        self.add_hook('post_switch',
1795
1883
            "Called after a checkout switches branch. "
1796
1884
            "post_switch is called with a "
1797
 
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
 
1885
            "bzrlib.branch.SwitchHookParams.", (2, 2))
1798
1886
 
1799
1887
 
1800
1888
 
1803
1891
 
1804
1892
 
1805
1893
class ChangeBranchTipParams(object):
1806
 
    """Object holding parameters passed to *_change_branch_tip hooks.
 
1894
    """Object holding parameters passed to `*_change_branch_tip` hooks.
1807
1895
 
1808
1896
    There are 5 fields that hooks may wish to access:
1809
1897
 
1841
1929
 
1842
1930
 
1843
1931
class BranchInitHookParams(object):
1844
 
    """Object holding parameters passed to *_branch_init hooks.
 
1932
    """Object holding parameters passed to `*_branch_init` hooks.
1845
1933
 
1846
1934
    There are 4 fields that hooks may wish to access:
1847
1935
 
1877
1965
        return self.__dict__ == other.__dict__
1878
1966
 
1879
1967
    def __repr__(self):
1880
 
        if self.branch:
1881
 
            return "<%s of %s>" % (self.__class__.__name__, self.branch)
1882
 
        else:
1883
 
            return "<%s of format:%s bzrdir:%s>" % (
1884
 
                self.__class__.__name__, self.branch,
1885
 
                self.format, self.bzrdir)
 
1968
        return "<%s of %s>" % (self.__class__.__name__, self.branch)
1886
1969
 
1887
1970
 
1888
1971
class SwitchHookParams(object):
1889
 
    """Object holding parameters passed to *_switch hooks.
 
1972
    """Object holding parameters passed to `*_switch` hooks.
1890
1973
 
1891
1974
    There are 4 fields that hooks may wish to access:
1892
1975
 
1918
2001
            self.revision_id)
1919
2002
 
1920
2003
 
1921
 
class BzrBranchFormat4(BranchFormat):
1922
 
    """Bzr branch format 4.
1923
 
 
1924
 
    This format has:
1925
 
     - a revision-history file.
1926
 
     - a branch-lock lock file [ to be shared with the bzrdir ]
1927
 
    """
1928
 
 
1929
 
    def get_format_description(self):
1930
 
        """See BranchFormat.get_format_description()."""
1931
 
        return "Branch format 4"
1932
 
 
1933
 
    def initialize(self, a_bzrdir, name=None):
1934
 
        """Create a branch of this format in a_bzrdir."""
1935
 
        utf8_files = [('revision-history', ''),
1936
 
                      ('branch-name', ''),
1937
 
                      ]
1938
 
        return self._initialize_helper(a_bzrdir, utf8_files, name=name,
1939
 
                                       lock_type='branch4', set_format=False)
1940
 
 
1941
 
    def __init__(self):
1942
 
        super(BzrBranchFormat4, self).__init__()
1943
 
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
1944
 
 
1945
 
    def network_name(self):
1946
 
        """The network name for this format is the control dirs disk label."""
1947
 
        return self._matchingbzrdir.get_format_string()
1948
 
 
1949
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1950
 
        """See BranchFormat.open()."""
1951
 
        if not _found:
1952
 
            # we are being called directly and must probe.
1953
 
            raise NotImplementedError
1954
 
        return BzrBranch(_format=self,
1955
 
                         _control_files=a_bzrdir._control_files,
1956
 
                         a_bzrdir=a_bzrdir,
1957
 
                         name=name,
1958
 
                         _repository=a_bzrdir.open_repository())
1959
 
 
1960
 
    def __str__(self):
1961
 
        return "Bazaar-NG branch format 4"
1962
 
 
1963
 
 
1964
2004
class BranchFormatMetadir(BranchFormat):
1965
2005
    """Common logic for meta-dir based branch formats."""
1966
2006
 
1968
2008
        """What class to instantiate on open calls."""
1969
2009
        raise NotImplementedError(self._branch_class)
1970
2010
 
 
2011
    def _get_initial_config(self, append_revisions_only=None):
 
2012
        if append_revisions_only:
 
2013
            return "append_revisions_only = True\n"
 
2014
        else:
 
2015
            # Avoid writing anything if append_revisions_only is disabled,
 
2016
            # as that is the default.
 
2017
            return ""
 
2018
 
 
2019
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
2020
                           repository=None):
 
2021
        """Initialize a branch in a bzrdir, with specified files
 
2022
 
 
2023
        :param a_bzrdir: The bzrdir to initialize the branch in
 
2024
        :param utf8_files: The files to create as a list of
 
2025
            (filename, content) tuples
 
2026
        :param name: Name of colocated branch to create, if any
 
2027
        :return: a branch in this format
 
2028
        """
 
2029
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
2030
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
 
2031
        control_files = lockable_files.LockableFiles(branch_transport,
 
2032
            'lock', lockdir.LockDir)
 
2033
        control_files.create_lock()
 
2034
        control_files.lock_write()
 
2035
        try:
 
2036
            utf8_files += [('format', self.get_format_string())]
 
2037
            for (filename, content) in utf8_files:
 
2038
                branch_transport.put_bytes(
 
2039
                    filename, content,
 
2040
                    mode=a_bzrdir._get_file_mode())
 
2041
        finally:
 
2042
            control_files.unlock()
 
2043
        branch = self.open(a_bzrdir, name, _found=True,
 
2044
                found_repository=repository)
 
2045
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
2046
        return branch
 
2047
 
1971
2048
    def network_name(self):
1972
2049
        """A simple byte string uniquely identifying this format for RPC calls.
1973
2050
 
1975
2052
        """
1976
2053
        return self.get_format_string()
1977
2054
 
1978
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
 
2055
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
 
2056
            found_repository=None):
1979
2057
        """See BranchFormat.open()."""
1980
2058
        if not _found:
1981
2059
            format = BranchFormat.find_format(a_bzrdir, name=name)
1986
2064
        try:
1987
2065
            control_files = lockable_files.LockableFiles(transport, 'lock',
1988
2066
                                                         lockdir.LockDir)
 
2067
            if found_repository is None:
 
2068
                found_repository = a_bzrdir.find_repository()
1989
2069
            return self._branch_class()(_format=self,
1990
2070
                              _control_files=control_files,
1991
2071
                              name=name,
1992
2072
                              a_bzrdir=a_bzrdir,
1993
 
                              _repository=a_bzrdir.find_repository(),
 
2073
                              _repository=found_repository,
1994
2074
                              ignore_fallbacks=ignore_fallbacks)
1995
2075
        except errors.NoSuchFile:
1996
2076
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2003
2083
    def supports_tags(self):
2004
2084
        return True
2005
2085
 
 
2086
    def supports_leaving_lock(self):
 
2087
        return True
 
2088
 
2006
2089
 
2007
2090
class BzrBranchFormat5(BranchFormatMetadir):
2008
2091
    """Bzr branch format 5.
2028
2111
        """See BranchFormat.get_format_description()."""
2029
2112
        return "Branch format 5"
2030
2113
 
2031
 
    def initialize(self, a_bzrdir, name=None):
 
2114
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2115
                   append_revisions_only=None):
2032
2116
        """Create a branch of this format in a_bzrdir."""
 
2117
        if append_revisions_only:
 
2118
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2033
2119
        utf8_files = [('revision-history', ''),
2034
2120
                      ('branch-name', ''),
2035
2121
                      ]
2036
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2122
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2037
2123
 
2038
2124
    def supports_tags(self):
2039
2125
        return False
2061
2147
        """See BranchFormat.get_format_description()."""
2062
2148
        return "Branch format 6"
2063
2149
 
2064
 
    def initialize(self, a_bzrdir, name=None):
 
2150
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2151
                   append_revisions_only=None):
2065
2152
        """Create a branch of this format in a_bzrdir."""
2066
2153
        utf8_files = [('last-revision', '0 null:\n'),
2067
 
                      ('branch.conf', ''),
 
2154
                      ('branch.conf',
 
2155
                          self._get_initial_config(append_revisions_only)),
2068
2156
                      ('tags', ''),
2069
2157
                      ]
2070
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2158
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2071
2159
 
2072
2160
    def make_tags(self, branch):
2073
2161
        """See bzrlib.branch.BranchFormat.make_tags()."""
2074
 
        return BasicTags(branch)
 
2162
        return _mod_tag.BasicTags(branch)
2075
2163
 
2076
2164
    def supports_set_append_revisions_only(self):
2077
2165
        return True
2091
2179
        """See BranchFormat.get_format_description()."""
2092
2180
        return "Branch format 8"
2093
2181
 
2094
 
    def initialize(self, a_bzrdir, name=None):
 
2182
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2183
                   append_revisions_only=None):
2095
2184
        """Create a branch of this format in a_bzrdir."""
2096
2185
        utf8_files = [('last-revision', '0 null:\n'),
2097
 
                      ('branch.conf', ''),
 
2186
                      ('branch.conf',
 
2187
                          self._get_initial_config(append_revisions_only)),
2098
2188
                      ('tags', ''),
2099
2189
                      ('references', '')
2100
2190
                      ]
2101
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2102
 
 
2103
 
    def __init__(self):
2104
 
        super(BzrBranchFormat8, self).__init__()
2105
 
        self._matchingbzrdir.repository_format = \
2106
 
            RepositoryFormatKnitPack5RichRoot()
 
2191
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2107
2192
 
2108
2193
    def make_tags(self, branch):
2109
2194
        """See bzrlib.branch.BranchFormat.make_tags()."""
2110
 
        return BasicTags(branch)
 
2195
        return _mod_tag.BasicTags(branch)
2111
2196
 
2112
2197
    def supports_set_append_revisions_only(self):
2113
2198
        return True
2118
2203
    supports_reference_locations = True
2119
2204
 
2120
2205
 
2121
 
class BzrBranchFormat7(BzrBranchFormat8):
 
2206
class BzrBranchFormat7(BranchFormatMetadir):
2122
2207
    """Branch format with last-revision, tags, and a stacked location pointer.
2123
2208
 
2124
2209
    The stacked location pointer is passed down to the repository and requires
2127
2212
    This format was introduced in bzr 1.6.
2128
2213
    """
2129
2214
 
2130
 
    def initialize(self, a_bzrdir, name=None):
 
2215
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2216
                   append_revisions_only=None):
2131
2217
        """Create a branch of this format in a_bzrdir."""
2132
2218
        utf8_files = [('last-revision', '0 null:\n'),
2133
 
                      ('branch.conf', ''),
 
2219
                      ('branch.conf',
 
2220
                          self._get_initial_config(append_revisions_only)),
2134
2221
                      ('tags', ''),
2135
2222
                      ]
2136
 
        return self._initialize_helper(a_bzrdir, utf8_files, name)
 
2223
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2137
2224
 
2138
2225
    def _branch_class(self):
2139
2226
        return BzrBranch7
2149
2236
    def supports_set_append_revisions_only(self):
2150
2237
        return True
2151
2238
 
 
2239
    def supports_stacking(self):
 
2240
        return True
 
2241
 
 
2242
    def make_tags(self, branch):
 
2243
        """See bzrlib.branch.BranchFormat.make_tags()."""
 
2244
        return _mod_tag.BasicTags(branch)
 
2245
 
2152
2246
    supports_reference_locations = False
2153
2247
 
2154
2248
 
2181
2275
        transport = a_bzrdir.get_branch_transport(None, name=name)
2182
2276
        location = transport.put_bytes('location', to_branch.base)
2183
2277
 
2184
 
    def initialize(self, a_bzrdir, name=None, target_branch=None):
 
2278
    def initialize(self, a_bzrdir, name=None, target_branch=None,
 
2279
            repository=None, append_revisions_only=None):
2185
2280
        """Create a branch of this format in a_bzrdir."""
2186
2281
        if target_branch is None:
2187
2282
            # this format does not implement branch itself, thus the implicit
2188
2283
            # creation contract must see it as uninitializable
2189
2284
            raise errors.UninitializableFormat(self)
2190
2285
        mutter('creating branch reference in %s', a_bzrdir.user_url)
 
2286
        if a_bzrdir._format.fixed_components:
 
2287
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
2191
2288
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2192
2289
        branch_transport.put_bytes('location',
2193
2290
            target_branch.bzrdir.user_url)
2215
2312
        return clone
2216
2313
 
2217
2314
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2218
 
             possible_transports=None, ignore_fallbacks=False):
 
2315
             possible_transports=None, ignore_fallbacks=False,
 
2316
             found_repository=None):
2219
2317
        """Return the branch that the branch reference in a_bzrdir points at.
2220
2318
 
2221
2319
        :param a_bzrdir: A BzrDir that contains a branch.
2252
2350
        return result
2253
2351
 
2254
2352
 
 
2353
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
 
2354
    """Branch format registry."""
 
2355
 
 
2356
    def __init__(self, other_registry=None):
 
2357
        super(BranchFormatRegistry, self).__init__(other_registry)
 
2358
        self._default_format = None
 
2359
 
 
2360
    def set_default(self, format):
 
2361
        self._default_format = format
 
2362
 
 
2363
    def get_default(self):
 
2364
        return self._default_format
 
2365
 
 
2366
 
2255
2367
network_format_registry = registry.FormatRegistry()
2256
2368
"""Registry of formats indexed by their network name.
2257
2369
 
2260
2372
BranchFormat.network_name() for more detail.
2261
2373
"""
2262
2374
 
 
2375
format_registry = BranchFormatRegistry(network_format_registry)
 
2376
 
2263
2377
 
2264
2378
# formats which have no format string are not discoverable
2265
2379
# and not independently creatable, so are not registered.
2267
2381
__format6 = BzrBranchFormat6()
2268
2382
__format7 = BzrBranchFormat7()
2269
2383
__format8 = BzrBranchFormat8()
2270
 
BranchFormat.register_format(__format5)
2271
 
BranchFormat.register_format(BranchReferenceFormat())
2272
 
BranchFormat.register_format(__format6)
2273
 
BranchFormat.register_format(__format7)
2274
 
BranchFormat.register_format(__format8)
2275
 
BranchFormat.set_default_format(__format7)
2276
 
_legacy_formats = [BzrBranchFormat4(),
2277
 
    ]
2278
 
network_format_registry.register(
2279
 
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
 
2384
format_registry.register(__format5)
 
2385
format_registry.register(BranchReferenceFormat())
 
2386
format_registry.register(__format6)
 
2387
format_registry.register(__format7)
 
2388
format_registry.register(__format8)
 
2389
format_registry.set_default(__format7)
2280
2390
 
2281
2391
 
2282
2392
class BranchWriteLockResult(LogicalLockResult):
2349
2459
    base = property(_get_base, doc="The URL for the root of this branch.")
2350
2460
 
2351
2461
    def _get_config(self):
2352
 
        return TransportConfig(self._transport, 'branch.conf')
 
2462
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
2353
2463
 
2354
2464
    def is_locked(self):
2355
2465
        return self.control_files.is_locked()
2430
2540
        """See Branch.print_file."""
2431
2541
        return self.repository.print_file(file, revision_id)
2432
2542
 
2433
 
    def _write_revision_history(self, history):
2434
 
        """Factored out of set_revision_history.
2435
 
 
2436
 
        This performs the actual writing to disk.
2437
 
        It is intended to be called by BzrBranch5.set_revision_history."""
2438
 
        self._transport.put_bytes(
2439
 
            'revision-history', '\n'.join(history),
2440
 
            mode=self.bzrdir._get_file_mode())
2441
 
 
2442
 
    @needs_write_lock
2443
 
    def set_revision_history(self, rev_history):
2444
 
        """See Branch.set_revision_history."""
2445
 
        if 'evil' in debug.debug_flags:
2446
 
            mutter_callsite(3, "set_revision_history scales with history.")
2447
 
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2448
 
        for rev_id in rev_history:
2449
 
            check_not_reserved_id(rev_id)
2450
 
        if Branch.hooks['post_change_branch_tip']:
2451
 
            # Don't calculate the last_revision_info() if there are no hooks
2452
 
            # that will use it.
2453
 
            old_revno, old_revid = self.last_revision_info()
2454
 
        if len(rev_history) == 0:
2455
 
            revid = _mod_revision.NULL_REVISION
2456
 
        else:
2457
 
            revid = rev_history[-1]
2458
 
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2459
 
        self._write_revision_history(rev_history)
2460
 
        self._clear_cached_state()
2461
 
        self._cache_revision_history(rev_history)
2462
 
        for hook in Branch.hooks['set_rh']:
2463
 
            hook(self, rev_history)
2464
 
        if Branch.hooks['post_change_branch_tip']:
2465
 
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2466
 
 
2467
 
    def _synchronize_history(self, destination, revision_id):
2468
 
        """Synchronize last revision and revision history between branches.
2469
 
 
2470
 
        This version is most efficient when the destination is also a
2471
 
        BzrBranch5, but works for BzrBranch6 as long as the revision
2472
 
        history is the true lefthand parent history, and all of the revisions
2473
 
        are in the destination's repository.  If not, set_revision_history
2474
 
        will fail.
2475
 
 
2476
 
        :param destination: The branch to copy the history into
2477
 
        :param revision_id: The revision-id to truncate history at.  May
2478
 
          be None to copy complete history.
2479
 
        """
2480
 
        if not isinstance(destination._format, BzrBranchFormat5):
2481
 
            super(BzrBranch, self)._synchronize_history(
2482
 
                destination, revision_id)
2483
 
            return
2484
 
        if revision_id == _mod_revision.NULL_REVISION:
2485
 
            new_history = []
2486
 
        else:
2487
 
            new_history = self.revision_history()
2488
 
        if revision_id is not None and new_history != []:
2489
 
            try:
2490
 
                new_history = new_history[:new_history.index(revision_id) + 1]
2491
 
            except ValueError:
2492
 
                rev = self.repository.get_revision(revision_id)
2493
 
                new_history = rev.get_history(self.repository)[1:]
2494
 
        destination.set_revision_history(new_history)
2495
 
 
2496
2543
    @needs_write_lock
2497
2544
    def set_last_revision_info(self, revno, revision_id):
2498
 
        """Set the last revision of this branch.
2499
 
 
2500
 
        The caller is responsible for checking that the revno is correct
2501
 
        for this revision id.
2502
 
 
2503
 
        It may be possible to set the branch last revision to an id not
2504
 
        present in the repository.  However, branches can also be
2505
 
        configured to check constraints on history, in which case this may not
2506
 
        be permitted.
2507
 
        """
 
2545
        if not revision_id or not isinstance(revision_id, basestring):
 
2546
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2508
2547
        revision_id = _mod_revision.ensure_null(revision_id)
2509
 
        # this old format stores the full history, but this api doesn't
2510
 
        # provide it, so we must generate, and might as well check it's
2511
 
        # correct
2512
 
        history = self._lefthand_history(revision_id)
2513
 
        if len(history) != revno:
2514
 
            raise AssertionError('%d != %d' % (len(history), revno))
2515
 
        self.set_revision_history(history)
2516
 
 
2517
 
    def _gen_revision_history(self):
2518
 
        history = self._transport.get_bytes('revision-history').split('\n')
2519
 
        if history[-1:] == ['']:
2520
 
            # There shouldn't be a trailing newline, but just in case.
2521
 
            history.pop()
2522
 
        return history
2523
 
 
2524
 
    @needs_write_lock
2525
 
    def generate_revision_history(self, revision_id, last_rev=None,
2526
 
        other_branch=None):
2527
 
        """Create a new revision history that will finish with revision_id.
2528
 
 
2529
 
        :param revision_id: the new tip to use.
2530
 
        :param last_rev: The previous last_revision. If not None, then this
2531
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
2532
 
        :param other_branch: The other branch that DivergedBranches should
2533
 
            raise with respect to.
2534
 
        """
2535
 
        self.set_revision_history(self._lefthand_history(revision_id,
2536
 
            last_rev, other_branch))
 
2548
        old_revno, old_revid = self.last_revision_info()
 
2549
        if self.get_append_revisions_only():
 
2550
            self._check_history_violation(revision_id)
 
2551
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
 
2552
        self._write_last_revision_info(revno, revision_id)
 
2553
        self._clear_cached_state()
 
2554
        self._last_revision_info_cache = revno, revision_id
 
2555
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2537
2556
 
2538
2557
    def basis_tree(self):
2539
2558
        """See Branch.basis_tree."""
2548
2567
                pass
2549
2568
        return None
2550
2569
 
2551
 
    def _basic_push(self, target, overwrite, stop_revision):
2552
 
        """Basic implementation of push without bound branches or hooks.
2553
 
 
2554
 
        Must be called with source read locked and target write locked.
2555
 
        """
2556
 
        result = BranchPushResult()
2557
 
        result.source_branch = self
2558
 
        result.target_branch = target
2559
 
        result.old_revno, result.old_revid = target.last_revision_info()
2560
 
        self.update_references(target)
2561
 
        if result.old_revid != self.last_revision():
2562
 
            # We assume that during 'push' this repository is closer than
2563
 
            # the target.
2564
 
            graph = self.repository.get_graph(target.repository)
2565
 
            target.update_revisions(self, stop_revision,
2566
 
                overwrite=overwrite, graph=graph)
2567
 
        if self._push_should_merge_tags():
2568
 
            result.tag_conflicts = self.tags.merge_to(target.tags,
2569
 
                overwrite)
2570
 
        result.new_revno, result.new_revid = target.last_revision_info()
2571
 
        return result
2572
 
 
2573
2570
    def get_stacked_on_url(self):
2574
2571
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2575
2572
 
2586
2583
            self._transport.put_bytes('parent', url + '\n',
2587
2584
                mode=self.bzrdir._get_file_mode())
2588
2585
 
2589
 
 
2590
 
class BzrBranch5(BzrBranch):
2591
 
    """A format 5 branch. This supports new features over plain branches.
2592
 
 
2593
 
    It has support for a master_branch which is the data for bound branches.
2594
 
    """
2595
 
 
2596
 
    def get_bound_location(self):
2597
 
        try:
2598
 
            return self._transport.get_bytes('bound')[:-1]
2599
 
        except errors.NoSuchFile:
2600
 
            return None
2601
 
 
2602
 
    @needs_read_lock
2603
 
    def get_master_branch(self, possible_transports=None):
2604
 
        """Return the branch we are bound to.
2605
 
 
2606
 
        :return: Either a Branch, or None
2607
 
 
2608
 
        This could memoise the branch, but if thats done
2609
 
        it must be revalidated on each new lock.
2610
 
        So for now we just don't memoise it.
2611
 
        # RBC 20060304 review this decision.
2612
 
        """
2613
 
        bound_loc = self.get_bound_location()
2614
 
        if not bound_loc:
2615
 
            return None
2616
 
        try:
2617
 
            return Branch.open(bound_loc,
2618
 
                               possible_transports=possible_transports)
2619
 
        except (errors.NotBranchError, errors.ConnectionError), e:
2620
 
            raise errors.BoundBranchConnectionFailure(
2621
 
                    self, bound_loc, e)
2622
 
 
2623
2586
    @needs_write_lock
2624
 
    def set_bound_location(self, location):
2625
 
        """Set the target where this branch is bound to.
2626
 
 
2627
 
        :param location: URL to the target branch
2628
 
        """
2629
 
        if location:
2630
 
            self._transport.put_bytes('bound', location+'\n',
2631
 
                mode=self.bzrdir._get_file_mode())
2632
 
        else:
2633
 
            try:
2634
 
                self._transport.delete('bound')
2635
 
            except errors.NoSuchFile:
2636
 
                return False
2637
 
            return True
 
2587
    def unbind(self):
 
2588
        """If bound, unbind"""
 
2589
        return self.set_bound_location(None)
2638
2590
 
2639
2591
    @needs_write_lock
2640
2592
    def bind(self, other):
2662
2614
        # history around
2663
2615
        self.set_bound_location(other.base)
2664
2616
 
 
2617
    def get_bound_location(self):
 
2618
        try:
 
2619
            return self._transport.get_bytes('bound')[:-1]
 
2620
        except errors.NoSuchFile:
 
2621
            return None
 
2622
 
 
2623
    @needs_read_lock
 
2624
    def get_master_branch(self, possible_transports=None):
 
2625
        """Return the branch we are bound to.
 
2626
 
 
2627
        :return: Either a Branch, or None
 
2628
        """
 
2629
        if self._master_branch_cache is None:
 
2630
            self._master_branch_cache = self._get_master_branch(
 
2631
                possible_transports)
 
2632
        return self._master_branch_cache
 
2633
 
 
2634
    def _get_master_branch(self, possible_transports):
 
2635
        bound_loc = self.get_bound_location()
 
2636
        if not bound_loc:
 
2637
            return None
 
2638
        try:
 
2639
            return Branch.open(bound_loc,
 
2640
                               possible_transports=possible_transports)
 
2641
        except (errors.NotBranchError, errors.ConnectionError), e:
 
2642
            raise errors.BoundBranchConnectionFailure(
 
2643
                    self, bound_loc, e)
 
2644
 
2665
2645
    @needs_write_lock
2666
 
    def unbind(self):
2667
 
        """If bound, unbind"""
2668
 
        return self.set_bound_location(None)
 
2646
    def set_bound_location(self, location):
 
2647
        """Set the target where this branch is bound to.
 
2648
 
 
2649
        :param location: URL to the target branch
 
2650
        """
 
2651
        self._master_branch_cache = None
 
2652
        if location:
 
2653
            self._transport.put_bytes('bound', location+'\n',
 
2654
                mode=self.bzrdir._get_file_mode())
 
2655
        else:
 
2656
            try:
 
2657
                self._transport.delete('bound')
 
2658
            except errors.NoSuchFile:
 
2659
                return False
 
2660
            return True
2669
2661
 
2670
2662
    @needs_write_lock
2671
2663
    def update(self, possible_transports=None):
2684
2676
            return old_tip
2685
2677
        return None
2686
2678
 
2687
 
 
2688
 
class BzrBranch8(BzrBranch5):
 
2679
    def _read_last_revision_info(self):
 
2680
        revision_string = self._transport.get_bytes('last-revision')
 
2681
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
 
2682
        revision_id = cache_utf8.get_cached_utf8(revision_id)
 
2683
        revno = int(revno)
 
2684
        return revno, revision_id
 
2685
 
 
2686
    def _write_last_revision_info(self, revno, revision_id):
 
2687
        """Simply write out the revision id, with no checks.
 
2688
 
 
2689
        Use set_last_revision_info to perform this safely.
 
2690
 
 
2691
        Does not update the revision_history cache.
 
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
 
 
2699
class FullHistoryBzrBranch(BzrBranch):
 
2700
    """Bzr branch which contains the full revision history."""
 
2701
 
 
2702
    @needs_write_lock
 
2703
    def set_last_revision_info(self, revno, revision_id):
 
2704
        if not revision_id or not isinstance(revision_id, basestring):
 
2705
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2706
        revision_id = _mod_revision.ensure_null(revision_id)
 
2707
        # this old format stores the full history, but this api doesn't
 
2708
        # provide it, so we must generate, and might as well check it's
 
2709
        # correct
 
2710
        history = self._lefthand_history(revision_id)
 
2711
        if len(history) != revno:
 
2712
            raise AssertionError('%d != %d' % (len(history), revno))
 
2713
        self._set_revision_history(history)
 
2714
 
 
2715
    def _read_last_revision_info(self):
 
2716
        rh = self.revision_history()
 
2717
        revno = len(rh)
 
2718
        if revno:
 
2719
            return (revno, rh[-1])
 
2720
        else:
 
2721
            return (0, _mod_revision.NULL_REVISION)
 
2722
 
 
2723
    @deprecated_method(deprecated_in((2, 4, 0)))
 
2724
    @needs_write_lock
 
2725
    def set_revision_history(self, rev_history):
 
2726
        """See Branch.set_revision_history."""
 
2727
        self._set_revision_history(rev_history)
 
2728
 
 
2729
    def _set_revision_history(self, rev_history):
 
2730
        if 'evil' in debug.debug_flags:
 
2731
            mutter_callsite(3, "set_revision_history scales with history.")
 
2732
        check_not_reserved_id = _mod_revision.check_not_reserved_id
 
2733
        for rev_id in rev_history:
 
2734
            check_not_reserved_id(rev_id)
 
2735
        if Branch.hooks['post_change_branch_tip']:
 
2736
            # Don't calculate the last_revision_info() if there are no hooks
 
2737
            # that will use it.
 
2738
            old_revno, old_revid = self.last_revision_info()
 
2739
        if len(rev_history) == 0:
 
2740
            revid = _mod_revision.NULL_REVISION
 
2741
        else:
 
2742
            revid = rev_history[-1]
 
2743
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
 
2744
        self._write_revision_history(rev_history)
 
2745
        self._clear_cached_state()
 
2746
        self._cache_revision_history(rev_history)
 
2747
        for hook in Branch.hooks['set_rh']:
 
2748
            hook(self, rev_history)
 
2749
        if Branch.hooks['post_change_branch_tip']:
 
2750
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2751
 
 
2752
    def _write_revision_history(self, history):
 
2753
        """Factored out of set_revision_history.
 
2754
 
 
2755
        This performs the actual writing to disk.
 
2756
        It is intended to be called by set_revision_history."""
 
2757
        self._transport.put_bytes(
 
2758
            'revision-history', '\n'.join(history),
 
2759
            mode=self.bzrdir._get_file_mode())
 
2760
 
 
2761
    def _gen_revision_history(self):
 
2762
        history = self._transport.get_bytes('revision-history').split('\n')
 
2763
        if history[-1:] == ['']:
 
2764
            # There shouldn't be a trailing newline, but just in case.
 
2765
            history.pop()
 
2766
        return history
 
2767
 
 
2768
    def _synchronize_history(self, destination, revision_id):
 
2769
        if not isinstance(destination, FullHistoryBzrBranch):
 
2770
            super(BzrBranch, self)._synchronize_history(
 
2771
                destination, revision_id)
 
2772
            return
 
2773
        if revision_id == _mod_revision.NULL_REVISION:
 
2774
            new_history = []
 
2775
        else:
 
2776
            new_history = self.revision_history()
 
2777
        if revision_id is not None and new_history != []:
 
2778
            try:
 
2779
                new_history = new_history[:new_history.index(revision_id) + 1]
 
2780
            except ValueError:
 
2781
                rev = self.repository.get_revision(revision_id)
 
2782
                new_history = rev.get_history(self.repository)[1:]
 
2783
        destination._set_revision_history(new_history)
 
2784
 
 
2785
    @needs_write_lock
 
2786
    def generate_revision_history(self, revision_id, last_rev=None,
 
2787
        other_branch=None):
 
2788
        """Create a new revision history that will finish with revision_id.
 
2789
 
 
2790
        :param revision_id: the new tip to use.
 
2791
        :param last_rev: The previous last_revision. If not None, then this
 
2792
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
2793
        :param other_branch: The other branch that DivergedBranches should
 
2794
            raise with respect to.
 
2795
        """
 
2796
        self._set_revision_history(self._lefthand_history(revision_id,
 
2797
            last_rev, other_branch))
 
2798
 
 
2799
 
 
2800
class BzrBranch5(FullHistoryBzrBranch):
 
2801
    """A format 5 branch. This supports new features over plain branches.
 
2802
 
 
2803
    It has support for a master_branch which is the data for bound branches.
 
2804
    """
 
2805
 
 
2806
 
 
2807
class BzrBranch8(BzrBranch):
2689
2808
    """A branch that stores tree-reference locations."""
2690
2809
 
2691
2810
    def _open_hook(self):
2717
2836
        self._last_revision_info_cache = None
2718
2837
        self._reference_info = None
2719
2838
 
2720
 
    def _last_revision_info(self):
2721
 
        revision_string = self._transport.get_bytes('last-revision')
2722
 
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2723
 
        revision_id = cache_utf8.get_cached_utf8(revision_id)
2724
 
        revno = int(revno)
2725
 
        return revno, revision_id
2726
 
 
2727
 
    def _write_last_revision_info(self, revno, revision_id):
2728
 
        """Simply write out the revision id, with no checks.
2729
 
 
2730
 
        Use set_last_revision_info to perform this safely.
2731
 
 
2732
 
        Does not update the revision_history cache.
2733
 
        Intended to be called by set_last_revision_info and
2734
 
        _write_revision_history.
2735
 
        """
2736
 
        revision_id = _mod_revision.ensure_null(revision_id)
2737
 
        out_string = '%d %s\n' % (revno, revision_id)
2738
 
        self._transport.put_bytes('last-revision', out_string,
2739
 
            mode=self.bzrdir._get_file_mode())
2740
 
 
2741
 
    @needs_write_lock
2742
 
    def set_last_revision_info(self, revno, revision_id):
2743
 
        revision_id = _mod_revision.ensure_null(revision_id)
2744
 
        old_revno, old_revid = self.last_revision_info()
2745
 
        if self._get_append_revisions_only():
2746
 
            self._check_history_violation(revision_id)
2747
 
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2748
 
        self._write_last_revision_info(revno, revision_id)
2749
 
        self._clear_cached_state()
2750
 
        self._last_revision_info_cache = revno, revision_id
2751
 
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2752
 
 
2753
 
    def _synchronize_history(self, destination, revision_id):
2754
 
        """Synchronize last revision and revision history between branches.
2755
 
 
2756
 
        :see: Branch._synchronize_history
2757
 
        """
2758
 
        # XXX: The base Branch has a fast implementation of this method based
2759
 
        # on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
2760
 
        # that uses set_revision_history.  This class inherits from BzrBranch5,
2761
 
        # but wants the fast implementation, so it calls
2762
 
        # Branch._synchronize_history directly.
2763
 
        Branch._synchronize_history(self, destination, revision_id)
2764
 
 
2765
2839
    def _check_history_violation(self, revision_id):
2766
 
        last_revision = _mod_revision.ensure_null(self.last_revision())
 
2840
        current_revid = self.last_revision()
 
2841
        last_revision = _mod_revision.ensure_null(current_revid)
2767
2842
        if _mod_revision.is_null(last_revision):
2768
2843
            return
2769
 
        if last_revision not in self._lefthand_history(revision_id):
2770
 
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
 
2844
        graph = self.repository.get_graph()
 
2845
        for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
 
2846
            if lh_ancestor == current_revid:
 
2847
                return
 
2848
        raise errors.AppendRevisionsOnlyViolation(self.user_url)
2771
2849
 
2772
2850
    def _gen_revision_history(self):
2773
2851
        """Generate the revision history from last revision
2776
2854
        self._extend_partial_history(stop_index=last_revno-1)
2777
2855
        return list(reversed(self._partial_revision_history_cache))
2778
2856
 
2779
 
    def _write_revision_history(self, history):
2780
 
        """Factored out of set_revision_history.
2781
 
 
2782
 
        This performs the actual writing to disk, with format-specific checks.
2783
 
        It is intended to be called by BzrBranch5.set_revision_history.
2784
 
        """
2785
 
        if len(history) == 0:
2786
 
            last_revision = 'null:'
2787
 
        else:
2788
 
            if history != self._lefthand_history(history[-1]):
2789
 
                raise errors.NotLefthandHistory(history)
2790
 
            last_revision = history[-1]
2791
 
        if self._get_append_revisions_only():
2792
 
            self._check_history_violation(last_revision)
2793
 
        self._write_last_revision_info(len(history), last_revision)
2794
 
 
2795
2857
    @needs_write_lock
2796
2858
    def _set_parent_location(self, url):
2797
2859
        """Set the parent branch"""
2883
2945
 
2884
2946
    def set_bound_location(self, location):
2885
2947
        """See Branch.set_push_location."""
 
2948
        self._master_branch_cache = None
2886
2949
        result = None
2887
2950
        config = self.get_config()
2888
2951
        if location is None:
2919
2982
        # you can always ask for the URL; but you might not be able to use it
2920
2983
        # if the repo can't support stacking.
2921
2984
        ## self._check_stackable_repo()
2922
 
        stacked_url = self._get_config_location('stacked_on_location')
 
2985
        # stacked_on_location is only ever defined in branch.conf, so don't
 
2986
        # waste effort reading the whole stack of config files.
 
2987
        config = self.get_config()._get_branch_data_config()
 
2988
        stacked_url = self._get_config_location('stacked_on_location',
 
2989
            config=config)
2923
2990
        if stacked_url is None:
2924
2991
            raise errors.NotStacked(self)
2925
2992
        return stacked_url
2926
2993
 
2927
 
    def _get_append_revisions_only(self):
2928
 
        return self.get_config(
2929
 
            ).get_user_option_as_bool('append_revisions_only')
2930
 
 
2931
 
    @needs_write_lock
2932
 
    def generate_revision_history(self, revision_id, last_rev=None,
2933
 
                                  other_branch=None):
2934
 
        """See BzrBranch5.generate_revision_history"""
2935
 
        history = self._lefthand_history(revision_id, last_rev, other_branch)
2936
 
        revno = len(history)
2937
 
        self.set_last_revision_info(revno, revision_id)
2938
 
 
2939
2994
    @needs_read_lock
2940
2995
    def get_rev_id(self, revno, history=None):
2941
2996
        """Find the revision id of the specified revno."""
2965
3020
        try:
2966
3021
            index = self._partial_revision_history_cache.index(revision_id)
2967
3022
        except ValueError:
2968
 
            self._extend_partial_history(stop_revision=revision_id)
 
3023
            try:
 
3024
                self._extend_partial_history(stop_revision=revision_id)
 
3025
            except errors.RevisionNotPresent, e:
 
3026
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
2969
3027
            index = len(self._partial_revision_history_cache) - 1
2970
3028
            if self._partial_revision_history_cache[index] != revision_id:
2971
3029
                raise errors.NoSuchRevision(self, revision_id)
3024
3082
    :ivar local_branch: target branch if there is a Master, else None
3025
3083
    :ivar target_branch: Target/destination branch object. (write locked)
3026
3084
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
 
3085
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3027
3086
    """
3028
3087
 
 
3088
    @deprecated_method(deprecated_in((2, 3, 0)))
3029
3089
    def __int__(self):
3030
 
        # DEPRECATED: pull used to return the change in revno
 
3090
        """Return the relative change in revno.
 
3091
 
 
3092
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3093
        """
3031
3094
        return self.new_revno - self.old_revno
3032
3095
 
3033
3096
    def report(self, to_file):
 
3097
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
3098
        tag_updates = getattr(self, "tag_updates", None)
3034
3099
        if not is_quiet():
3035
 
            if self.old_revid == self.new_revid:
3036
 
                to_file.write('No revisions to pull.\n')
3037
 
            else:
 
3100
            if self.old_revid != self.new_revid:
3038
3101
                to_file.write('Now on revision %d.\n' % self.new_revno)
 
3102
            if tag_updates:
 
3103
                to_file.write('%d tag(s) updated.\n' % len(tag_updates))
 
3104
            if self.old_revid == self.new_revid and not tag_updates:
 
3105
                if not tag_conflicts:
 
3106
                    to_file.write('No revisions or tags to pull.\n')
 
3107
                else:
 
3108
                    to_file.write('No revisions to pull.\n')
3039
3109
        self._show_tag_conficts(to_file)
3040
3110
 
3041
3111
 
3058
3128
        target, otherwise it will be None.
3059
3129
    """
3060
3130
 
 
3131
    @deprecated_method(deprecated_in((2, 3, 0)))
3061
3132
    def __int__(self):
3062
 
        # DEPRECATED: push used to return the change in revno
 
3133
        """Return the relative change in revno.
 
3134
 
 
3135
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3136
        """
3063
3137
        return self.new_revno - self.old_revno
3064
3138
 
3065
3139
    def report(self, to_file):
3066
 
        """Write a human-readable description of the result."""
3067
 
        if self.old_revid == self.new_revid:
3068
 
            note('No new revisions to push.')
3069
 
        else:
3070
 
            note('Pushed up to revision %d.' % self.new_revno)
 
3140
        # TODO: This function gets passed a to_file, but then
 
3141
        # ignores it and calls note() instead. This is also
 
3142
        # inconsistent with PullResult(), which writes to stdout.
 
3143
        # -- JRV20110901, bug #838853
 
3144
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
3145
        tag_updates = getattr(self, "tag_updates", None)
 
3146
        if not is_quiet():
 
3147
            if self.old_revid != self.new_revid:
 
3148
                note(gettext('Pushed up to revision %d.') % self.new_revno)
 
3149
            if tag_updates:
 
3150
                note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
 
3151
            if self.old_revid == self.new_revid and not tag_updates:
 
3152
                if not tag_conflicts:
 
3153
                    note(gettext('No new revisions or tags to push.'))
 
3154
                else:
 
3155
                    note(gettext('No new revisions to push.'))
3071
3156
        self._show_tag_conficts(to_file)
3072
3157
 
3073
3158
 
3087
3172
        :param verbose: Requests more detailed display of what was checked,
3088
3173
            if any.
3089
3174
        """
3090
 
        note('checked branch %s format %s', self.branch.user_url,
3091
 
            self.branch._format)
 
3175
        note(gettext('checked branch {0} format {1}').format(
 
3176
                                self.branch.user_url, self.branch._format))
3092
3177
        for error in self.errors:
3093
 
            note('found error:%s', error)
 
3178
            note(gettext('found error:%s'), error)
3094
3179
 
3095
3180
 
3096
3181
class Converter5to6(object):
3135
3220
 
3136
3221
 
3137
3222
class Converter7to8(object):
3138
 
    """Perform an in-place upgrade of format 6 to format 7"""
 
3223
    """Perform an in-place upgrade of format 7 to format 8"""
3139
3224
 
3140
3225
    def convert(self, branch):
3141
3226
        format = BzrBranchFormat8()
3144
3229
        branch._transport.put_bytes('format', format.get_format_string())
3145
3230
 
3146
3231
 
3147
 
def _run_with_write_locked_target(target, callable, *args, **kwargs):
3148
 
    """Run ``callable(*args, **kwargs)``, write-locking target for the
3149
 
    duration.
3150
 
 
3151
 
    _run_with_write_locked_target will attempt to release the lock it acquires.
3152
 
 
3153
 
    If an exception is raised by callable, then that exception *will* be
3154
 
    propagated, even if the unlock attempt raises its own error.  Thus
3155
 
    _run_with_write_locked_target should be preferred to simply doing::
3156
 
 
3157
 
        target.lock_write()
3158
 
        try:
3159
 
            return callable(*args, **kwargs)
3160
 
        finally:
3161
 
            target.unlock()
3162
 
 
3163
 
    """
3164
 
    # This is very similar to bzrlib.decorators.needs_write_lock.  Perhaps they
3165
 
    # should share code?
3166
 
    target.lock_write()
3167
 
    try:
3168
 
        result = callable(*args, **kwargs)
3169
 
    except:
3170
 
        exc_info = sys.exc_info()
3171
 
        try:
3172
 
            target.unlock()
3173
 
        finally:
3174
 
            raise exc_info[0], exc_info[1], exc_info[2]
3175
 
    else:
3176
 
        target.unlock()
3177
 
        return result
3178
 
 
3179
 
 
3180
3232
class InterBranch(InterObject):
3181
3233
    """This class represents operations taking place between two branches.
3182
3234
 
3188
3240
    _optimisers = []
3189
3241
    """The available optimised InterBranch types."""
3190
3242
 
3191
 
    @staticmethod
3192
 
    def _get_branch_formats_to_test():
3193
 
        """Return a tuple with the Branch formats to use when testing."""
3194
 
        raise NotImplementedError(InterBranch._get_branch_formats_to_test)
 
3243
    @classmethod
 
3244
    def _get_branch_formats_to_test(klass):
 
3245
        """Return an iterable of format tuples for testing.
 
3246
        
 
3247
        :return: An iterable of (from_format, to_format) to use when testing
 
3248
            this InterBranch class. Each InterBranch class should define this
 
3249
            method itself.
 
3250
        """
 
3251
        raise NotImplementedError(klass._get_branch_formats_to_test)
3195
3252
 
 
3253
    @needs_write_lock
3196
3254
    def pull(self, overwrite=False, stop_revision=None,
3197
3255
             possible_transports=None, local=False):
3198
3256
        """Mirror source into target branch.
3203
3261
        """
3204
3262
        raise NotImplementedError(self.pull)
3205
3263
 
3206
 
    def update_revisions(self, stop_revision=None, overwrite=False,
3207
 
                         graph=None):
3208
 
        """Pull in new perfect-fit revisions.
3209
 
 
3210
 
        :param stop_revision: Updated until the given revision
3211
 
        :param overwrite: Always set the branch pointer, rather than checking
3212
 
            to see if it is a proper descendant.
3213
 
        :param graph: A Graph object that can be used to query history
3214
 
            information. This can be None.
3215
 
        :return: None
3216
 
        """
3217
 
        raise NotImplementedError(self.update_revisions)
3218
 
 
3219
 
    def push(self, overwrite=False, stop_revision=None,
 
3264
    @needs_write_lock
 
3265
    def push(self, overwrite=False, stop_revision=None, lossy=False,
3220
3266
             _override_hook_source_branch=None):
3221
3267
        """Mirror the source branch into the target branch.
3222
3268
 
3224
3270
        """
3225
3271
        raise NotImplementedError(self.push)
3226
3272
 
 
3273
    @needs_write_lock
 
3274
    def copy_content_into(self, revision_id=None):
 
3275
        """Copy the content of source into target
 
3276
 
 
3277
        revision_id: if not None, the revision history in the new branch will
 
3278
                     be truncated to end with revision_id.
 
3279
        """
 
3280
        raise NotImplementedError(self.copy_content_into)
 
3281
 
 
3282
    @needs_write_lock
 
3283
    def fetch(self, stop_revision=None, limit=None):
 
3284
        """Fetch revisions.
 
3285
 
 
3286
        :param stop_revision: Last revision to fetch
 
3287
        :param limit: Optional rough limit of revisions to fetch
 
3288
        """
 
3289
        raise NotImplementedError(self.fetch)
 
3290
 
3227
3291
 
3228
3292
class GenericInterBranch(InterBranch):
3229
 
    """InterBranch implementation that uses public Branch functions.
3230
 
    """
3231
 
 
3232
 
    @staticmethod
3233
 
    def _get_branch_formats_to_test():
3234
 
        return BranchFormat._default_format, BranchFormat._default_format
3235
 
 
3236
 
    def update_revisions(self, stop_revision=None, overwrite=False,
3237
 
        graph=None):
3238
 
        """See InterBranch.update_revisions()."""
 
3293
    """InterBranch implementation that uses public Branch functions."""
 
3294
 
 
3295
    @classmethod
 
3296
    def is_compatible(klass, source, target):
 
3297
        # GenericBranch uses the public API, so always compatible
 
3298
        return True
 
3299
 
 
3300
    @classmethod
 
3301
    def _get_branch_formats_to_test(klass):
 
3302
        return [(format_registry.get_default(), format_registry.get_default())]
 
3303
 
 
3304
    @classmethod
 
3305
    def unwrap_format(klass, format):
 
3306
        if isinstance(format, remote.RemoteBranchFormat):
 
3307
            format._ensure_real()
 
3308
            return format._custom_format
 
3309
        return format
 
3310
 
 
3311
    @needs_write_lock
 
3312
    def copy_content_into(self, revision_id=None):
 
3313
        """Copy the content of source into target
 
3314
 
 
3315
        revision_id: if not None, the revision history in the new branch will
 
3316
                     be truncated to end with revision_id.
 
3317
        """
 
3318
        self.source.update_references(self.target)
 
3319
        self.source._synchronize_history(self.target, revision_id)
 
3320
        try:
 
3321
            parent = self.source.get_parent()
 
3322
        except errors.InaccessibleParent, e:
 
3323
            mutter('parent was not accessible to copy: %s', e)
 
3324
        else:
 
3325
            if parent:
 
3326
                self.target.set_parent(parent)
 
3327
        if self.source._push_should_merge_tags():
 
3328
            self.source.tags.merge_to(self.target.tags)
 
3329
 
 
3330
    @needs_write_lock
 
3331
    def fetch(self, stop_revision=None, limit=None):
 
3332
        if self.target.base == self.source.base:
 
3333
            return (0, [])
3239
3334
        self.source.lock_read()
3240
3335
        try:
3241
 
            other_revno, other_last_revision = self.source.last_revision_info()
3242
 
            stop_revno = None # unknown
3243
 
            if stop_revision is None:
3244
 
                stop_revision = other_last_revision
3245
 
                if _mod_revision.is_null(stop_revision):
3246
 
                    # if there are no commits, we're done.
3247
 
                    return
3248
 
                stop_revno = other_revno
3249
 
 
3250
 
            # what's the current last revision, before we fetch [and change it
3251
 
            # possibly]
3252
 
            last_rev = _mod_revision.ensure_null(self.target.last_revision())
3253
 
            # we fetch here so that we don't process data twice in the common
3254
 
            # case of having something to pull, and so that the check for
3255
 
            # already merged can operate on the just fetched graph, which will
3256
 
            # be cached in memory.
3257
 
            self.target.fetch(self.source, stop_revision)
3258
 
            # Check to see if one is an ancestor of the other
3259
 
            if not overwrite:
3260
 
                if graph is None:
3261
 
                    graph = self.target.repository.get_graph()
3262
 
                if self.target._check_if_descendant_or_diverged(
3263
 
                        stop_revision, last_rev, graph, self.source):
3264
 
                    # stop_revision is a descendant of last_rev, but we aren't
3265
 
                    # overwriting, so we're done.
3266
 
                    return
3267
 
            if stop_revno is None:
3268
 
                if graph is None:
3269
 
                    graph = self.target.repository.get_graph()
3270
 
                this_revno, this_last_revision = \
3271
 
                        self.target.last_revision_info()
3272
 
                stop_revno = graph.find_distance_to_null(stop_revision,
3273
 
                                [(other_last_revision, other_revno),
3274
 
                                 (this_last_revision, this_revno)])
3275
 
            self.target.set_last_revision_info(stop_revno, stop_revision)
 
3336
            fetch_spec_factory = fetch.FetchSpecFactory()
 
3337
            fetch_spec_factory.source_branch = self.source
 
3338
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
 
3339
            fetch_spec_factory.source_repo = self.source.repository
 
3340
            fetch_spec_factory.target_repo = self.target.repository
 
3341
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
 
3342
            fetch_spec_factory.limit = limit
 
3343
            fetch_spec = fetch_spec_factory.make_fetch_spec()
 
3344
            return self.target.repository.fetch(self.source.repository,
 
3345
                fetch_spec=fetch_spec)
3276
3346
        finally:
3277
3347
            self.source.unlock()
3278
3348
 
 
3349
    @needs_write_lock
 
3350
    def _update_revisions(self, stop_revision=None, overwrite=False,
 
3351
            graph=None):
 
3352
        other_revno, other_last_revision = self.source.last_revision_info()
 
3353
        stop_revno = None # unknown
 
3354
        if stop_revision is None:
 
3355
            stop_revision = other_last_revision
 
3356
            if _mod_revision.is_null(stop_revision):
 
3357
                # if there are no commits, we're done.
 
3358
                return
 
3359
            stop_revno = other_revno
 
3360
 
 
3361
        # what's the current last revision, before we fetch [and change it
 
3362
        # possibly]
 
3363
        last_rev = _mod_revision.ensure_null(self.target.last_revision())
 
3364
        # we fetch here so that we don't process data twice in the common
 
3365
        # case of having something to pull, and so that the check for
 
3366
        # already merged can operate on the just fetched graph, which will
 
3367
        # be cached in memory.
 
3368
        self.fetch(stop_revision=stop_revision)
 
3369
        # Check to see if one is an ancestor of the other
 
3370
        if not overwrite:
 
3371
            if graph is None:
 
3372
                graph = self.target.repository.get_graph()
 
3373
            if self.target._check_if_descendant_or_diverged(
 
3374
                    stop_revision, last_rev, graph, self.source):
 
3375
                # stop_revision is a descendant of last_rev, but we aren't
 
3376
                # overwriting, so we're done.
 
3377
                return
 
3378
        if stop_revno is None:
 
3379
            if graph is None:
 
3380
                graph = self.target.repository.get_graph()
 
3381
            this_revno, this_last_revision = \
 
3382
                    self.target.last_revision_info()
 
3383
            stop_revno = graph.find_distance_to_null(stop_revision,
 
3384
                            [(other_last_revision, other_revno),
 
3385
                             (this_last_revision, this_revno)])
 
3386
        self.target.set_last_revision_info(stop_revno, stop_revision)
 
3387
 
 
3388
    @needs_write_lock
3279
3389
    def pull(self, overwrite=False, stop_revision=None,
 
3390
             possible_transports=None, run_hooks=True,
 
3391
             _override_hook_target=None, local=False):
 
3392
        """Pull from source into self, updating my master if any.
 
3393
 
 
3394
        :param run_hooks: Private parameter - if false, this branch
 
3395
            is being called because it's the master of the primary branch,
 
3396
            so it should not run its hooks.
 
3397
        """
 
3398
        bound_location = self.target.get_bound_location()
 
3399
        if local and not bound_location:
 
3400
            raise errors.LocalRequiresBoundBranch()
 
3401
        master_branch = None
 
3402
        source_is_master = False
 
3403
        if bound_location:
 
3404
            # bound_location comes from a config file, some care has to be
 
3405
            # taken to relate it to source.user_url
 
3406
            normalized = urlutils.normalize_url(bound_location)
 
3407
            try:
 
3408
                relpath = self.source.user_transport.relpath(normalized)
 
3409
                source_is_master = (relpath == '')
 
3410
            except (errors.PathNotChild, errors.InvalidURL):
 
3411
                source_is_master = False
 
3412
        if not local and bound_location and not source_is_master:
 
3413
            # not pulling from master, so we need to update master.
 
3414
            master_branch = self.target.get_master_branch(possible_transports)
 
3415
            master_branch.lock_write()
 
3416
        try:
 
3417
            if master_branch:
 
3418
                # pull from source into master.
 
3419
                master_branch.pull(self.source, overwrite, stop_revision,
 
3420
                    run_hooks=False)
 
3421
            return self._pull(overwrite,
 
3422
                stop_revision, _hook_master=master_branch,
 
3423
                run_hooks=run_hooks,
 
3424
                _override_hook_target=_override_hook_target,
 
3425
                merge_tags_to_master=not source_is_master)
 
3426
        finally:
 
3427
            if master_branch:
 
3428
                master_branch.unlock()
 
3429
 
 
3430
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
3431
             _override_hook_source_branch=None):
 
3432
        """See InterBranch.push.
 
3433
 
 
3434
        This is the basic concrete implementation of push()
 
3435
 
 
3436
        :param _override_hook_source_branch: If specified, run the hooks
 
3437
            passing this Branch as the source, rather than self.  This is for
 
3438
            use of RemoteBranch, where push is delegated to the underlying
 
3439
            vfs-based Branch.
 
3440
        """
 
3441
        if lossy:
 
3442
            raise errors.LossyPushToSameVCS(self.source, self.target)
 
3443
        # TODO: Public option to disable running hooks - should be trivial but
 
3444
        # needs tests.
 
3445
 
 
3446
        op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
 
3447
        op.add_cleanup(self.source.lock_read().unlock)
 
3448
        op.add_cleanup(self.target.lock_write().unlock)
 
3449
        return op.run(overwrite, stop_revision,
 
3450
            _override_hook_source_branch=_override_hook_source_branch)
 
3451
 
 
3452
    def _basic_push(self, overwrite, stop_revision):
 
3453
        """Basic implementation of push without bound branches or hooks.
 
3454
 
 
3455
        Must be called with source read locked and target write locked.
 
3456
        """
 
3457
        result = BranchPushResult()
 
3458
        result.source_branch = self.source
 
3459
        result.target_branch = self.target
 
3460
        result.old_revno, result.old_revid = self.target.last_revision_info()
 
3461
        self.source.update_references(self.target)
 
3462
        if result.old_revid != stop_revision:
 
3463
            # We assume that during 'push' this repository is closer than
 
3464
            # the target.
 
3465
            graph = self.source.repository.get_graph(self.target.repository)
 
3466
            self._update_revisions(stop_revision, overwrite=overwrite,
 
3467
                    graph=graph)
 
3468
        if self.source._push_should_merge_tags():
 
3469
            result.tag_updates, result.tag_conflicts = (
 
3470
                self.source.tags.merge_to(self.target.tags, overwrite))
 
3471
        result.new_revno, result.new_revid = self.target.last_revision_info()
 
3472
        return result
 
3473
 
 
3474
    def _push_with_bound_branches(self, operation, overwrite, stop_revision,
 
3475
            _override_hook_source_branch=None):
 
3476
        """Push from source into target, and into target's master if any.
 
3477
        """
 
3478
        def _run_hooks():
 
3479
            if _override_hook_source_branch:
 
3480
                result.source_branch = _override_hook_source_branch
 
3481
            for hook in Branch.hooks['post_push']:
 
3482
                hook(result)
 
3483
 
 
3484
        bound_location = self.target.get_bound_location()
 
3485
        if bound_location and self.target.base != bound_location:
 
3486
            # there is a master branch.
 
3487
            #
 
3488
            # XXX: Why the second check?  Is it even supported for a branch to
 
3489
            # be bound to itself? -- mbp 20070507
 
3490
            master_branch = self.target.get_master_branch()
 
3491
            master_branch.lock_write()
 
3492
            operation.add_cleanup(master_branch.unlock)
 
3493
            # push into the master from the source branch.
 
3494
            master_inter = InterBranch.get(self.source, master_branch)
 
3495
            master_inter._basic_push(overwrite, stop_revision)
 
3496
            # and push into the target branch from the source. Note that
 
3497
            # we push from the source branch again, because it's considered
 
3498
            # the highest bandwidth repository.
 
3499
            result = self._basic_push(overwrite, stop_revision)
 
3500
            result.master_branch = master_branch
 
3501
            result.local_branch = self.target
 
3502
        else:
 
3503
            master_branch = None
 
3504
            # no master branch
 
3505
            result = self._basic_push(overwrite, stop_revision)
 
3506
            # TODO: Why set master_branch and local_branch if there's no
 
3507
            # binding?  Maybe cleaner to just leave them unset? -- mbp
 
3508
            # 20070504
 
3509
            result.master_branch = self.target
 
3510
            result.local_branch = None
 
3511
        _run_hooks()
 
3512
        return result
 
3513
 
 
3514
    def _pull(self, overwrite=False, stop_revision=None,
3280
3515
             possible_transports=None, _hook_master=None, run_hooks=True,
3281
 
             _override_hook_target=None, local=False):
 
3516
             _override_hook_target=None, local=False,
 
3517
             merge_tags_to_master=True):
3282
3518
        """See Branch.pull.
3283
3519
 
 
3520
        This function is the core worker, used by GenericInterBranch.pull to
 
3521
        avoid duplication when pulling source->master and source->local.
 
3522
 
3284
3523
        :param _hook_master: Private parameter - set the branch to
3285
3524
            be supplied as the master to pull hooks.
3286
3525
        :param run_hooks: Private parameter - if false, this branch
3287
3526
            is being called because it's the master of the primary branch,
3288
3527
            so it should not run its hooks.
 
3528
            is being called because it's the master of the primary branch,
 
3529
            so it should not run its hooks.
3289
3530
        :param _override_hook_target: Private parameter - set the branch to be
3290
3531
            supplied as the target_branch to pull hooks.
3291
3532
        :param local: Only update the local branch, and not the bound branch.
3310
3551
            # -- JRV20090506
3311
3552
            result.old_revno, result.old_revid = \
3312
3553
                self.target.last_revision_info()
3313
 
            self.target.update_revisions(self.source, stop_revision,
3314
 
                overwrite=overwrite, graph=graph)
 
3554
            self._update_revisions(stop_revision, overwrite=overwrite,
 
3555
                graph=graph)
3315
3556
            # TODO: The old revid should be specified when merging tags, 
3316
3557
            # so a tags implementation that versions tags can only 
3317
3558
            # pull in the most recent changes. -- JRV20090506
3318
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3319
 
                overwrite)
 
3559
            result.tag_updates, result.tag_conflicts = (
 
3560
                self.source.tags.merge_to(self.target.tags, overwrite,
 
3561
                    ignore_master=not merge_tags_to_master))
3320
3562
            result.new_revno, result.new_revid = self.target.last_revision_info()
3321
3563
            if _hook_master:
3322
3564
                result.master_branch = _hook_master
3331
3573
            self.source.unlock()
3332
3574
        return result
3333
3575
 
3334
 
    def push(self, overwrite=False, stop_revision=None,
3335
 
             _override_hook_source_branch=None):
3336
 
        """See InterBranch.push.
3337
 
 
3338
 
        This is the basic concrete implementation of push()
3339
 
 
3340
 
        :param _override_hook_source_branch: If specified, run
3341
 
        the hooks passing this Branch as the source, rather than self.
3342
 
        This is for use of RemoteBranch, where push is delegated to the
3343
 
        underlying vfs-based Branch.
3344
 
        """
3345
 
        # TODO: Public option to disable running hooks - should be trivial but
3346
 
        # needs tests.
3347
 
        self.source.lock_read()
3348
 
        try:
3349
 
            return _run_with_write_locked_target(
3350
 
                self.target, self._push_with_bound_branches, overwrite,
3351
 
                stop_revision,
3352
 
                _override_hook_source_branch=_override_hook_source_branch)
3353
 
        finally:
3354
 
            self.source.unlock()
3355
 
 
3356
 
    def _push_with_bound_branches(self, overwrite, stop_revision,
3357
 
            _override_hook_source_branch=None):
3358
 
        """Push from source into target, and into target's master if any.
3359
 
        """
3360
 
        def _run_hooks():
3361
 
            if _override_hook_source_branch:
3362
 
                result.source_branch = _override_hook_source_branch
3363
 
            for hook in Branch.hooks['post_push']:
3364
 
                hook(result)
3365
 
 
3366
 
        bound_location = self.target.get_bound_location()
3367
 
        if bound_location and self.target.base != bound_location:
3368
 
            # there is a master branch.
3369
 
            #
3370
 
            # XXX: Why the second check?  Is it even supported for a branch to
3371
 
            # be bound to itself? -- mbp 20070507
3372
 
            master_branch = self.target.get_master_branch()
3373
 
            master_branch.lock_write()
3374
 
            try:
3375
 
                # push into the master from the source branch.
3376
 
                self.source._basic_push(master_branch, overwrite, stop_revision)
3377
 
                # and push into the target branch from the source. Note that we
3378
 
                # push from the source branch again, because its considered the
3379
 
                # highest bandwidth repository.
3380
 
                result = self.source._basic_push(self.target, overwrite,
3381
 
                    stop_revision)
3382
 
                result.master_branch = master_branch
3383
 
                result.local_branch = self.target
3384
 
                _run_hooks()
3385
 
                return result
3386
 
            finally:
3387
 
                master_branch.unlock()
3388
 
        else:
3389
 
            # no master branch
3390
 
            result = self.source._basic_push(self.target, overwrite,
3391
 
                stop_revision)
3392
 
            # TODO: Why set master_branch and local_branch if there's no
3393
 
            # binding?  Maybe cleaner to just leave them unset? -- mbp
3394
 
            # 20070504
3395
 
            result.master_branch = self.target
3396
 
            result.local_branch = None
3397
 
            _run_hooks()
3398
 
            return result
3399
 
 
3400
 
    @classmethod
3401
 
    def is_compatible(self, source, target):
3402
 
        # GenericBranch uses the public API, so always compatible
3403
 
        return True
3404
 
 
3405
 
 
3406
 
class InterToBranch5(GenericInterBranch):
3407
 
 
3408
 
    @staticmethod
3409
 
    def _get_branch_formats_to_test():
3410
 
        return BranchFormat._default_format, BzrBranchFormat5()
3411
 
 
3412
 
    def pull(self, overwrite=False, stop_revision=None,
3413
 
             possible_transports=None, run_hooks=True,
3414
 
             _override_hook_target=None, local=False):
3415
 
        """Pull from source into self, updating my master if any.
3416
 
 
3417
 
        :param run_hooks: Private parameter - if false, this branch
3418
 
            is being called because it's the master of the primary branch,
3419
 
            so it should not run its hooks.
3420
 
        """
3421
 
        bound_location = self.target.get_bound_location()
3422
 
        if local and not bound_location:
3423
 
            raise errors.LocalRequiresBoundBranch()
3424
 
        master_branch = None
3425
 
        if not local and bound_location and self.source.user_url != bound_location:
3426
 
            # not pulling from master, so we need to update master.
3427
 
            master_branch = self.target.get_master_branch(possible_transports)
3428
 
            master_branch.lock_write()
3429
 
        try:
3430
 
            if master_branch:
3431
 
                # pull from source into master.
3432
 
                master_branch.pull(self.source, overwrite, stop_revision,
3433
 
                    run_hooks=False)
3434
 
            return super(InterToBranch5, self).pull(overwrite,
3435
 
                stop_revision, _hook_master=master_branch,
3436
 
                run_hooks=run_hooks,
3437
 
                _override_hook_target=_override_hook_target)
3438
 
        finally:
3439
 
            if master_branch:
3440
 
                master_branch.unlock()
3441
 
 
3442
3576
 
3443
3577
InterBranch.register_optimiser(GenericInterBranch)
3444
 
InterBranch.register_optimiser(InterToBranch5)