~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-05-21 13:36:51 UTC
  • mfrom: (5243.2.1 readdir_cleanup)
  • Revision ID: pqm@pqm.ubuntu.com-20100521133651-p62dndo2giy5ls21
(lifeless) Some cleanups to the readdir pyrex code for a little efficiency
 and to avoid compile warnings. (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
16
16
 
17
17
 
18
18
from cStringIO import StringIO
 
19
import sys
19
20
 
20
21
from bzrlib.lazy_import import lazy_import
21
22
lazy_import(globals(), """
22
 
import itertools
 
23
from itertools import chain
23
24
from bzrlib import (
24
25
        bzrdir,
25
26
        cache_utf8,
26
 
        cleanup,
27
27
        config as _mod_config,
28
28
        debug,
29
29
        errors,
30
 
        fetch,
31
 
        graph as _mod_graph,
32
30
        lockdir,
33
31
        lockable_files,
34
 
        remote,
35
32
        repository,
36
33
        revision as _mod_revision,
37
34
        rio,
38
 
        tag as _mod_tag,
 
35
        symbol_versioning,
39
36
        transport,
 
37
        tsort,
40
38
        ui,
41
39
        urlutils,
42
40
        )
43
 
from bzrlib.i18n import gettext, ngettext
 
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
    )
44
47
""")
45
48
 
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
 
49
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
 
50
from bzrlib.hooks import HookPoint, Hooks
55
51
from bzrlib.inter import InterObject
56
52
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
57
53
from bzrlib import registry
62
58
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
63
59
 
64
60
 
65
 
class Branch(controldir.ControlComponent):
 
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):
66
67
    """Branch holding a history of revisions.
67
68
 
68
69
    :ivar base:
69
70
        Base directory/url of the branch; using control_url and
70
71
        control_transport is more standardized.
71
 
    :ivar hooks: An instance of BranchHooks.
72
 
    :ivar _master_branch_cache: cached result of get_master_branch, see
73
 
        _clear_cached_state.
 
72
 
 
73
    hooks: An instance of BranchHooks.
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
94
93
        self._last_revision_info_cache = None
95
 
        self._master_branch_cache = None
96
94
        self._merge_sorted_revisions_cache = None
97
95
        self._open_hook()
98
96
        hooks = Branch.hooks['open']
104
102
 
105
103
    def _activate_fallback_location(self, url):
106
104
        """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
114
105
        repo = self._get_fallback_repository(url)
115
106
        if repo.has_same_location(self.repository):
116
107
            raise errors.UnstackableLocationError(self.user_url, url)
206
197
        return self.supports_tags() and self.tags.get_tag_dict()
207
198
 
208
199
    def get_config(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)
 
200
        return BranchConfig(self)
227
201
 
228
202
    def _get_config(self):
229
203
        """Get the concrete config for just the config in this branch.
244
218
            possible_transports=[self.bzrdir.root_transport])
245
219
        return a_branch.repository
246
220
 
247
 
    @needs_read_lock
248
221
    def _get_tags_bytes(self):
249
222
        """Get the bytes of a serialised tags dict.
250
223
 
257
230
        :return: The bytes of the tags file.
258
231
        :seealso: Branch._set_tags_bytes.
259
232
        """
260
 
        if self._tags_bytes is None:
261
 
            self._tags_bytes = self._transport.get_bytes('tags')
262
 
        return self._tags_bytes
 
233
        return self._transport.get_bytes('tags')
263
234
 
264
235
    def _get_nick(self, local=False, possible_transports=None):
265
236
        config = self.get_config()
267
238
        if not local and not config.has_explicit_nickname():
268
239
            try:
269
240
                master = self.get_master_branch(possible_transports)
270
 
                if master and self.user_url == master.user_url:
271
 
                    raise errors.RecursiveBind(self.user_url)
272
241
                if master is not None:
273
242
                    # return the master branch value
274
243
                    return master.nick
275
 
            except errors.RecursiveBind, e:
276
 
                raise e
277
244
            except errors.BzrError, e:
278
245
                # Silently fall back to local implicit nick if the master is
279
246
                # unavailable
456
423
            after. If None, the rest of history is included.
457
424
        :param stop_rule: if stop_revision_id is not None, the precise rule
458
425
            to use for termination:
459
 
 
460
426
            * 'exclude' - leave the stop revision out of the result (default)
461
427
            * 'include' - the stop revision is the last item in the result
462
428
            * 'with-merges' - include the stop revision and all of its
464
430
            * 'with-merges-without-common-ancestry' - filter out revisions 
465
431
              that are in both ancestries
466
432
        :param direction: either 'reverse' or 'forward':
467
 
 
468
433
            * reverse means return the start_revision_id first, i.e.
469
434
              start at the most recent revision and go backwards in history
470
435
            * forward returns tuples in the opposite order to reverse.
514
479
        rev_iter = iter(merge_sorted_revisions)
515
480
        if start_revision_id is not None:
516
481
            for node in rev_iter:
517
 
                rev_id = node.key
 
482
                rev_id = node.key[-1]
518
483
                if rev_id != start_revision_id:
519
484
                    continue
520
485
                else:
521
486
                    # The decision to include the start or not
522
487
                    # depends on the stop_rule if a stop is provided
523
488
                    # so pop this node back into the iterator
524
 
                    rev_iter = itertools.chain(iter([node]), rev_iter)
 
489
                    rev_iter = chain(iter([node]), rev_iter)
525
490
                    break
526
491
        if stop_revision_id is None:
527
492
            # Yield everything
528
493
            for node in rev_iter:
529
 
                rev_id = node.key
 
494
                rev_id = node.key[-1]
530
495
                yield (rev_id, node.merge_depth, node.revno,
531
496
                       node.end_of_merge)
532
497
        elif stop_rule == 'exclude':
533
498
            for node in rev_iter:
534
 
                rev_id = node.key
 
499
                rev_id = node.key[-1]
535
500
                if rev_id == stop_revision_id:
536
501
                    return
537
502
                yield (rev_id, node.merge_depth, node.revno,
538
503
                       node.end_of_merge)
539
504
        elif stop_rule == 'include':
540
505
            for node in rev_iter:
541
 
                rev_id = node.key
 
506
                rev_id = node.key[-1]
542
507
                yield (rev_id, node.merge_depth, node.revno,
543
508
                       node.end_of_merge)
544
509
                if rev_id == stop_revision_id:
550
515
            ancestors = graph.find_unique_ancestors(start_revision_id,
551
516
                                                    [stop_revision_id])
552
517
            for node in rev_iter:
553
 
                rev_id = node.key
 
518
                rev_id = node.key[-1]
554
519
                if rev_id not in ancestors:
555
520
                    continue
556
521
                yield (rev_id, node.merge_depth, node.revno,
566
531
            reached_stop_revision_id = False
567
532
            revision_id_whitelist = []
568
533
            for node in rev_iter:
569
 
                rev_id = node.key
 
534
                rev_id = node.key[-1]
570
535
                if rev_id == left_parent:
571
536
                    # reached the left parent after the stop_revision
572
537
                    return
652
617
        """
653
618
        raise errors.UpgradeRequired(self.user_url)
654
619
 
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
 
 
663
620
    def set_append_revisions_only(self, enabled):
664
621
        if not self._format.supports_set_append_revisions_only():
665
622
            raise errors.UpgradeRequired(self.user_url)
679
636
        raise errors.UnsupportedOperation(self.get_reference_info, self)
680
637
 
681
638
    @needs_write_lock
682
 
    def fetch(self, from_branch, last_revision=None, limit=None):
 
639
    def fetch(self, from_branch, last_revision=None, pb=None):
683
640
        """Copy revisions from from_branch into this branch.
684
641
 
685
642
        :param from_branch: Where to copy from.
686
643
        :param last_revision: What revision to stop at (None for at the end
687
644
                              of the branch.
688
 
        :param limit: Optional rough limit of revisions to fetch
 
645
        :param pb: An optional progress bar to use.
689
646
        :return: None
690
647
        """
691
 
        return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
 
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()
692
664
 
693
665
    def get_bound_location(self):
694
666
        """Return the URL of the branch we are bound to.
705
677
 
706
678
    def get_commit_builder(self, parents, config=None, timestamp=None,
707
679
                           timezone=None, committer=None, revprops=None,
708
 
                           revision_id=None, lossy=False):
 
680
                           revision_id=None):
709
681
        """Obtain a CommitBuilder for this branch.
710
682
 
711
683
        :param parents: Revision ids of the parents of the new revision.
715
687
        :param committer: Optional committer to set for commit.
716
688
        :param revprops: Optional dictionary of revision properties.
717
689
        :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 
720
690
        """
721
691
 
722
692
        if config is None:
723
693
            config = self.get_config()
724
694
 
725
695
        return self.repository.get_commit_builder(self, parents, config,
726
 
            timestamp, timezone, committer, revprops, revision_id,
727
 
            lossy)
 
696
            timestamp, timezone, committer, revprops, revision_id)
728
697
 
729
698
    def get_master_branch(self, possible_transports=None):
730
699
        """Return the branch we are bound to.
757
726
        """Print `file` to stdout."""
758
727
        raise NotImplementedError(self.print_file)
759
728
 
760
 
    @deprecated_method(deprecated_in((2, 4, 0)))
761
729
    def set_revision_history(self, rev_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)
 
730
        raise NotImplementedError(self.set_revision_history)
808
731
 
809
732
    @needs_write_lock
810
733
    def set_parent(self, url):
854
777
 
855
778
    def _unstack(self):
856
779
        """Change a branch to be unstacked, copying data as needed.
857
 
 
 
780
        
858
781
        Don't call this directly, use set_stacked_on_url(None).
859
782
        """
860
783
        pb = ui.ui_factory.nested_progress_bar()
861
784
        try:
862
 
            pb.update(gettext("Unstacking"))
 
785
            pb.update("Unstacking")
863
786
            # The basic approach here is to fetch the tip of the branch,
864
787
            # including all available ghosts, from the existing stacked
865
788
            # repository into a new repository object without the fallbacks. 
869
792
            old_repository = self.repository
870
793
            if len(old_repository._fallback_repositories) != 1:
871
794
                raise AssertionError("can't cope with fallback repositories "
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()
 
795
                    "of %r" % (self.repository,))
 
796
            # unlock it, including unlocking the fallback
901
797
            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):
 
798
            old_repository.lock_read()
 
799
            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
920
817
                self.repository.lock_write()
921
 
            # Fetch from the old repository into the new.
922
 
            old_repository.lock_read()
923
 
            try:
924
818
                # XXX: If you unstack a branch while it has a working tree
925
819
                # with a pending merge, the pending-merged revisions will no
926
820
                # longer be present.  You can (probably) revert and remerge.
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)
 
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)
935
828
            finally:
936
829
                old_repository.unlock()
937
830
        finally:
942
835
 
943
836
        :seealso: Branch._get_tags_bytes.
944
837
        """
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)
 
838
        return _run_with_write_locked_target(self, self._transport.put_bytes,
 
839
            'tags', bytes)
952
840
 
953
841
    def _cache_revision_history(self, rev_history):
954
842
        """Set the cached revision history to rev_history.
981
869
        self._revision_history_cache = None
982
870
        self._revision_id_to_revno_cache = None
983
871
        self._last_revision_info_cache = None
984
 
        self._master_branch_cache = None
985
872
        self._merge_sorted_revisions_cache = None
986
873
        self._partial_revision_history_cache = []
987
874
        self._partial_revision_id_to_revno_cache = {}
988
 
        self._tags_bytes = None
989
875
 
990
876
    def _gen_revision_history(self):
991
877
        """Return sequence of revision hashes on to this branch.
1041
927
        :return: A tuple (revno, revision_id).
1042
928
        """
1043
929
        if self._last_revision_info_cache is None:
1044
 
            self._last_revision_info_cache = self._read_last_revision_info()
 
930
            self._last_revision_info_cache = self._last_revision_info()
1045
931
        return self._last_revision_info_cache
1046
932
 
1047
 
    def _read_last_revision_info(self):
1048
 
        raise NotImplementedError(self._read_last_revision_info)
1049
 
 
1050
 
    @deprecated_method(deprecated_in((2, 4, 0)))
 
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
 
1051
980
    def import_last_revision_info(self, source_repo, revno, revid):
1052
981
        """Set the last revision info, importing from another repo if necessary.
1053
982
 
 
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
 
1054
986
        :param source_repo: Source repository to optionally fetch from
1055
987
        :param revno: Revision number of the new tip
1056
988
        :param revid: Revision id of the new tip
1059
991
            self.repository.fetch(source_repo, revision_id=revid)
1060
992
        self.set_last_revision_info(revno, revid)
1061
993
 
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
 
 
1083
994
    def revision_id_to_revno(self, revision_id):
1084
995
        """Given a revision id, return its revno"""
1085
996
        if _mod_revision.is_null(revision_id):
1105
1016
            self._extend_partial_history(distance_from_last)
1106
1017
        return self._partial_revision_history_cache[distance_from_last]
1107
1018
 
 
1019
    @needs_write_lock
1108
1020
    def pull(self, source, overwrite=False, stop_revision=None,
1109
1021
             possible_transports=None, *args, **kwargs):
1110
1022
        """Mirror source into this branch.
1117
1029
            stop_revision=stop_revision,
1118
1030
            possible_transports=possible_transports, *args, **kwargs)
1119
1031
 
1120
 
    def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1121
 
            *args, **kwargs):
 
1032
    def push(self, target, overwrite=False, stop_revision=None, *args,
 
1033
        **kwargs):
1122
1034
        """Mirror this branch into target.
1123
1035
 
1124
1036
        This branch is considered to be 'local', having low latency.
1125
1037
        """
1126
1038
        return InterBranch.get(self, target).push(overwrite, stop_revision,
1127
 
            lossy, *args, **kwargs)
 
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)
1128
1061
 
1129
1062
    def basis_tree(self):
1130
1063
        """Return `Tree` object for last revision."""
1285
1218
        return result
1286
1219
 
1287
1220
    @needs_read_lock
1288
 
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
1289
 
            repository=None):
 
1221
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
1290
1222
        """Create a new line of development from the branch, into to_bzrdir.
1291
1223
 
1292
1224
        to_bzrdir controls the branch format.
1297
1229
        if (repository_policy is not None and
1298
1230
            repository_policy.requires_stacking()):
1299
1231
            to_bzrdir._format.require_stacking(_skip_repo=True)
1300
 
        result = to_bzrdir.create_branch(repository=repository)
 
1232
        result = to_bzrdir.create_branch()
1301
1233
        result.lock_write()
1302
1234
        try:
1303
1235
            if repository_policy is not None:
1304
1236
                repository_policy.configure_branch(result)
1305
1237
            self.copy_content_into(result, revision_id=revision_id)
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)
 
1238
            result.set_parent(self.bzrdir.root_transport.base)
1311
1239
        finally:
1312
1240
            result.unlock()
1313
1241
        return result
1337
1265
                revno = 1
1338
1266
        destination.set_last_revision_info(revno, revision_id)
1339
1267
 
 
1268
    @needs_read_lock
1340
1269
    def copy_content_into(self, destination, revision_id=None):
1341
1270
        """Copy the content of self into destination.
1342
1271
 
1343
1272
        revision_id: if not None, the revision history in the new branch will
1344
1273
                     be truncated to end with revision_id.
1345
1274
        """
1346
 
        return InterBranch.get(self, destination).copy_content_into(
1347
 
            revision_id=revision_id)
 
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)
1348
1286
 
1349
1287
    def update_references(self, target):
1350
1288
        if not getattr(self._format, 'supports_reference_locations', False):
1391
1329
        # specific check.
1392
1330
        return result
1393
1331
 
1394
 
    def _get_checkout_format(self, lightweight=False):
 
1332
    def _get_checkout_format(self):
1395
1333
        """Return the most suitable metadir for a checkout of this branch.
1396
1334
        Weaves are used if this branch's repository uses weaves.
1397
1335
        """
1398
 
        format = self.repository.bzrdir.checkout_metadir()
1399
 
        format.set_branch_format(self._format)
 
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)
1400
1343
        return format
1401
1344
 
1402
1345
    def create_clone_on_transport(self, to_transport, revision_id=None,
1403
 
        stacked_on=None, create_prefix=False, use_existing_dir=False,
1404
 
        no_tree=None):
 
1346
        stacked_on=None, create_prefix=False, use_existing_dir=False):
1405
1347
        """Create a clone of this branch and its bzrdir.
1406
1348
 
1407
1349
        :param to_transport: The transport to clone onto.
1420
1362
            revision_id = self.last_revision()
1421
1363
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1422
1364
            revision_id=revision_id, stacked_on=stacked_on,
1423
 
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
1424
 
            no_tree=no_tree)
 
1365
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1425
1366
        return dir_to.open_branch()
1426
1367
 
1427
1368
    def create_checkout(self, to_location, revision_id=None,
1432
1373
        :param to_location: The url to produce the checkout at
1433
1374
        :param revision_id: The revision to check out
1434
1375
        :param lightweight: If True, produce a lightweight checkout, otherwise,
1435
 
            produce a bound branch (heavyweight checkout)
 
1376
        produce a bound branch (heavyweight checkout)
1436
1377
        :param accelerator_tree: A tree which can be used for retrieving file
1437
1378
            contents more quickly than the revision tree, i.e. a workingtree.
1438
1379
            The revision tree will be used for cases where accelerator_tree's
1443
1384
        """
1444
1385
        t = transport.get_transport(to_location)
1445
1386
        t.ensure_base()
1446
 
        format = self._get_checkout_format(lightweight=lightweight)
1447
1387
        if lightweight:
 
1388
            format = self._get_checkout_format()
1448
1389
            checkout = format.initialize_on_transport(t)
1449
1390
            from_branch = BranchReferenceFormat().initialize(checkout, 
1450
1391
                target_branch=self)
1451
1392
        else:
 
1393
            format = self._get_checkout_format()
1452
1394
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1453
1395
                to_location, force_new_tree=False, format=format)
1454
1396
            checkout = checkout_branch.bzrdir
1483
1425
 
1484
1426
    def reference_parent(self, file_id, path, possible_transports=None):
1485
1427
        """Return the parent branch for a tree-reference file_id
1486
 
 
1487
1428
        :param file_id: The file_id of the tree reference
1488
1429
        :param path: The path of the file_id in the tree
1489
1430
        :return: A branch associated with the file_id
1542
1483
        else:
1543
1484
            raise AssertionError("invalid heads: %r" % (heads,))
1544
1485
 
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):
 
1486
 
 
1487
class BranchFormat(object):
1572
1488
    """An encapsulation of the initialization and open routines for a format.
1573
1489
 
1574
1490
    Formats provide three things:
1577
1493
     * an open routine.
1578
1494
 
1579
1495
    Formats are placed in an dict by their format string for reference
1580
 
    during branch opening. It's not required that these be instances, they
 
1496
    during branch opening. Its not required that these be instances, they
1581
1497
    can be classes themselves with class methods - it simply depends on
1582
1498
    whether state is needed for a given format or not.
1583
1499
 
1586
1502
    object will be created every time regardless.
1587
1503
    """
1588
1504
 
 
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
 
1589
1513
    def __eq__(self, other):
1590
1514
        return self.__class__ is other.__class__
1591
1515
 
1598
1522
        try:
1599
1523
            transport = a_bzrdir.get_branch_transport(None, name=name)
1600
1524
            format_string = transport.get_bytes("format")
1601
 
            return format_registry.get(format_string)
 
1525
            return klass._formats[format_string]
1602
1526
        except errors.NoSuchFile:
1603
1527
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1604
1528
        except KeyError:
1605
1529
            raise errors.UnknownFormatError(format=format_string, kind='branch')
1606
1530
 
1607
1531
    @classmethod
1608
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1609
1532
    def get_default_format(klass):
1610
1533
        """Return the current 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()
 
1534
        return klass._default_format
1622
1535
 
1623
1536
    def get_reference(self, a_bzrdir, name=None):
1624
1537
        """Get the target reference of the branch in a_bzrdir.
1663
1576
        for hook in hooks:
1664
1577
            hook(params)
1665
1578
 
1666
 
    def initialize(self, a_bzrdir, name=None, repository=None,
1667
 
                   append_revisions_only=None):
 
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):
1668
1625
        """Create a branch of this format in a_bzrdir.
1669
1626
        
1670
1627
        :param name: Name of the colocated branch to create.
1692
1649
        Note that it is normal for branch to be a RemoteBranch when using tags
1693
1650
        on a RemoteBranch.
1694
1651
        """
1695
 
        return _mod_tag.DisabledTags(branch)
 
1652
        return DisabledTags(branch)
1696
1653
 
1697
1654
    def network_name(self):
1698
1655
        """A simple byte string uniquely identifying this format for RPC calls.
1704
1661
        """
1705
1662
        raise NotImplementedError(self.network_name)
1706
1663
 
1707
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
1708
 
            found_repository=None):
 
1664
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1709
1665
        """Return the branch object for a_bzrdir
1710
1666
 
1711
1667
        :param a_bzrdir: A BzrDir that contains a branch.
1718
1674
        raise NotImplementedError(self.open)
1719
1675
 
1720
1676
    @classmethod
1721
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1722
1677
    def register_format(klass, format):
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)
 
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__)
1729
1683
 
1730
1684
    @classmethod
1731
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1732
1685
    def set_default_format(klass, format):
1733
 
        format_registry.set_default(format)
 
1686
        klass._default_format = format
1734
1687
 
1735
1688
    def supports_set_append_revisions_only(self):
1736
1689
        """True if this format supports set_append_revisions_only."""
1740
1693
        """True if this format records a stacked-on branch."""
1741
1694
        return False
1742
1695
 
1743
 
    def supports_leaving_lock(self):
1744
 
        """True if this format supports leaving locks in place."""
1745
 
        return False # by default
1746
 
 
1747
1696
    @classmethod
1748
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1749
1697
    def unregister_format(klass, format):
1750
 
        format_registry.remove(format)
 
1698
        del klass._formats[format.get_format_string()]
1751
1699
 
1752
1700
    def __str__(self):
1753
1701
        return self.get_format_description().rstrip()
1756
1704
        """True if this format supports tags stored in the branch"""
1757
1705
        return False  # by default
1758
1706
 
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
 
 
1795
1707
 
1796
1708
class BranchHooks(Hooks):
1797
1709
    """A dictionary mapping hook name to a list of callables for branch hooks.
1806
1718
        These are all empty initially, because by default nothing should get
1807
1719
        notified.
1808
1720
        """
1809
 
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1810
 
        self.add_hook('set_rh',
 
1721
        Hooks.__init__(self)
 
1722
        self.create_hook(HookPoint('set_rh',
1811
1723
            "Invoked whenever the revision history has been set via "
1812
1724
            "set_revision_history. The api signature is (branch, "
1813
1725
            "revision_history), and the branch will be write-locked. "
1814
1726
            "The set_rh hook can be expensive for bzr to trigger, a better "
1815
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
1816
 
        self.add_hook('open',
 
1727
            "hook to use is Branch.post_change_branch_tip.", (0, 15), None))
 
1728
        self.create_hook(HookPoint('open',
1817
1729
            "Called with the Branch object that has been opened after a "
1818
 
            "branch is opened.", (1, 8))
1819
 
        self.add_hook('post_push',
 
1730
            "branch is opened.", (1, 8), None))
 
1731
        self.create_hook(HookPoint('post_push',
1820
1732
            "Called after a push operation completes. post_push is called "
1821
1733
            "with a bzrlib.branch.BranchPushResult object and only runs in the "
1822
 
            "bzr client.", (0, 15))
1823
 
        self.add_hook('post_pull',
 
1734
            "bzr client.", (0, 15), None))
 
1735
        self.create_hook(HookPoint('post_pull',
1824
1736
            "Called after a pull operation completes. post_pull is called "
1825
1737
            "with a bzrlib.branch.PullResult object and only runs in the "
1826
 
            "bzr client.", (0, 15))
1827
 
        self.add_hook('pre_commit',
1828
 
            "Called after a commit is calculated but before it is "
 
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 "
1829
1741
            "completed. pre_commit is called with (local, master, old_revno, "
1830
1742
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1831
1743
            "). old_revid is NULL_REVISION for the first commit to a branch, "
1833
1745
            "basis revision. hooks MUST NOT modify this delta. "
1834
1746
            " future_tree is an in-memory tree obtained from "
1835
1747
            "CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1836
 
            "tree.", (0,91))
1837
 
        self.add_hook('post_commit',
 
1748
            "tree.", (0,91), None))
 
1749
        self.create_hook(HookPoint('post_commit',
1838
1750
            "Called in the bzr client after a commit has completed. "
1839
1751
            "post_commit is called with (local, master, old_revno, old_revid, "
1840
1752
            "new_revno, new_revid). old_revid is NULL_REVISION for the first "
1841
 
            "commit to a branch.", (0, 15))
1842
 
        self.add_hook('post_uncommit',
 
1753
            "commit to a branch.", (0, 15), None))
 
1754
        self.create_hook(HookPoint('post_uncommit',
1843
1755
            "Called in the bzr client after an uncommit completes. "
1844
1756
            "post_uncommit is called with (local, master, old_revno, "
1845
1757
            "old_revid, new_revno, new_revid) where local is the local branch "
1846
1758
            "or None, master is the target branch, and an empty branch "
1847
 
            "receives new_revno of 0, new_revid of None.", (0, 15))
1848
 
        self.add_hook('pre_change_branch_tip',
 
1759
            "receives new_revno of 0, new_revid of None.", (0, 15), None))
 
1760
        self.create_hook(HookPoint('pre_change_branch_tip',
1849
1761
            "Called in bzr client and server before a change to the tip of a "
1850
1762
            "branch is made. pre_change_branch_tip is called with a "
1851
1763
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1852
 
            "commit, uncommit will all trigger this hook.", (1, 6))
1853
 
        self.add_hook('post_change_branch_tip',
 
1764
            "commit, uncommit will all trigger this hook.", (1, 6), None))
 
1765
        self.create_hook(HookPoint('post_change_branch_tip',
1854
1766
            "Called in bzr client and server after a change to the tip of a "
1855
1767
            "branch is made. post_change_branch_tip is called with a "
1856
1768
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1857
 
            "commit, uncommit will all trigger this hook.", (1, 4))
1858
 
        self.add_hook('transform_fallback_location',
 
1769
            "commit, uncommit will all trigger this hook.", (1, 4), None))
 
1770
        self.create_hook(HookPoint('transform_fallback_location',
1859
1771
            "Called when a stacked branch is activating its fallback "
1860
1772
            "locations. transform_fallback_location is called with (branch, "
1861
1773
            "url), and should return a new url. Returning the same url "
1866
1778
            "fallback locations have not been activated. When there are "
1867
1779
            "multiple hooks installed for transform_fallback_location, "
1868
1780
            "all are called with the url returned from the previous hook."
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. "
 
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."
1872
1784
            "automatic_tag_name is called with (branch, revision_id) and "
1873
1785
            "should return a tag name or None if no tag name could be "
1874
1786
            "determined. The first non-None tag name returned will be used.",
1875
 
            (2, 2))
1876
 
        self.add_hook('post_branch_init',
 
1787
            (2, 2), None))
 
1788
        self.create_hook(HookPoint('post_branch_init',
1877
1789
            "Called after new branch initialization completes. "
1878
1790
            "post_branch_init is called with a "
1879
1791
            "bzrlib.branch.BranchInitHookParams. "
1880
1792
            "Note that init, branch and checkout (both heavyweight and "
1881
 
            "lightweight) will all trigger this hook.", (2, 2))
1882
 
        self.add_hook('post_switch',
 
1793
            "lightweight) will all trigger this hook.", (2, 2), None))
 
1794
        self.create_hook(HookPoint('post_switch',
1883
1795
            "Called after a checkout switches branch. "
1884
1796
            "post_switch is called with a "
1885
 
            "bzrlib.branch.SwitchHookParams.", (2, 2))
 
1797
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
1886
1798
 
1887
1799
 
1888
1800
 
1891
1803
 
1892
1804
 
1893
1805
class ChangeBranchTipParams(object):
1894
 
    """Object holding parameters passed to `*_change_branch_tip` hooks.
 
1806
    """Object holding parameters passed to *_change_branch_tip hooks.
1895
1807
 
1896
1808
    There are 5 fields that hooks may wish to access:
1897
1809
 
1929
1841
 
1930
1842
 
1931
1843
class BranchInitHookParams(object):
1932
 
    """Object holding parameters passed to `*_branch_init` hooks.
 
1844
    """Object holding parameters passed to *_branch_init hooks.
1933
1845
 
1934
1846
    There are 4 fields that hooks may wish to access:
1935
1847
 
1965
1877
        return self.__dict__ == other.__dict__
1966
1878
 
1967
1879
    def __repr__(self):
1968
 
        return "<%s of %s>" % (self.__class__.__name__, self.branch)
 
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)
1969
1886
 
1970
1887
 
1971
1888
class SwitchHookParams(object):
1972
 
    """Object holding parameters passed to `*_switch` hooks.
 
1889
    """Object holding parameters passed to *_switch hooks.
1973
1890
 
1974
1891
    There are 4 fields that hooks may wish to access:
1975
1892
 
2001
1918
            self.revision_id)
2002
1919
 
2003
1920
 
 
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
 
2004
1964
class BranchFormatMetadir(BranchFormat):
2005
1965
    """Common logic for meta-dir based branch formats."""
2006
1966
 
2008
1968
        """What class to instantiate on open calls."""
2009
1969
        raise NotImplementedError(self._branch_class)
2010
1970
 
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
 
 
2048
1971
    def network_name(self):
2049
1972
        """A simple byte string uniquely identifying this format for RPC calls.
2050
1973
 
2052
1975
        """
2053
1976
        return self.get_format_string()
2054
1977
 
2055
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2056
 
            found_repository=None):
 
1978
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
2057
1979
        """See BranchFormat.open()."""
2058
1980
        if not _found:
2059
1981
            format = BranchFormat.find_format(a_bzrdir, name=name)
2064
1986
        try:
2065
1987
            control_files = lockable_files.LockableFiles(transport, 'lock',
2066
1988
                                                         lockdir.LockDir)
2067
 
            if found_repository is None:
2068
 
                found_repository = a_bzrdir.find_repository()
2069
1989
            return self._branch_class()(_format=self,
2070
1990
                              _control_files=control_files,
2071
1991
                              name=name,
2072
1992
                              a_bzrdir=a_bzrdir,
2073
 
                              _repository=found_repository,
 
1993
                              _repository=a_bzrdir.find_repository(),
2074
1994
                              ignore_fallbacks=ignore_fallbacks)
2075
1995
        except errors.NoSuchFile:
2076
1996
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2083
2003
    def supports_tags(self):
2084
2004
        return True
2085
2005
 
2086
 
    def supports_leaving_lock(self):
2087
 
        return True
2088
 
 
2089
2006
 
2090
2007
class BzrBranchFormat5(BranchFormatMetadir):
2091
2008
    """Bzr branch format 5.
2111
2028
        """See BranchFormat.get_format_description()."""
2112
2029
        return "Branch format 5"
2113
2030
 
2114
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2115
 
                   append_revisions_only=None):
 
2031
    def initialize(self, a_bzrdir, name=None):
2116
2032
        """Create a branch of this format in a_bzrdir."""
2117
 
        if append_revisions_only:
2118
 
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2119
2033
        utf8_files = [('revision-history', ''),
2120
2034
                      ('branch-name', ''),
2121
2035
                      ]
2122
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2036
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2123
2037
 
2124
2038
    def supports_tags(self):
2125
2039
        return False
2147
2061
        """See BranchFormat.get_format_description()."""
2148
2062
        return "Branch format 6"
2149
2063
 
2150
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2151
 
                   append_revisions_only=None):
 
2064
    def initialize(self, a_bzrdir, name=None):
2152
2065
        """Create a branch of this format in a_bzrdir."""
2153
2066
        utf8_files = [('last-revision', '0 null:\n'),
2154
 
                      ('branch.conf',
2155
 
                          self._get_initial_config(append_revisions_only)),
 
2067
                      ('branch.conf', ''),
2156
2068
                      ('tags', ''),
2157
2069
                      ]
2158
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2070
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2159
2071
 
2160
2072
    def make_tags(self, branch):
2161
2073
        """See bzrlib.branch.BranchFormat.make_tags()."""
2162
 
        return _mod_tag.BasicTags(branch)
 
2074
        return BasicTags(branch)
2163
2075
 
2164
2076
    def supports_set_append_revisions_only(self):
2165
2077
        return True
2179
2091
        """See BranchFormat.get_format_description()."""
2180
2092
        return "Branch format 8"
2181
2093
 
2182
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2183
 
                   append_revisions_only=None):
 
2094
    def initialize(self, a_bzrdir, name=None):
2184
2095
        """Create a branch of this format in a_bzrdir."""
2185
2096
        utf8_files = [('last-revision', '0 null:\n'),
2186
 
                      ('branch.conf',
2187
 
                          self._get_initial_config(append_revisions_only)),
 
2097
                      ('branch.conf', ''),
2188
2098
                      ('tags', ''),
2189
2099
                      ('references', '')
2190
2100
                      ]
2191
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
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()
2192
2107
 
2193
2108
    def make_tags(self, branch):
2194
2109
        """See bzrlib.branch.BranchFormat.make_tags()."""
2195
 
        return _mod_tag.BasicTags(branch)
 
2110
        return BasicTags(branch)
2196
2111
 
2197
2112
    def supports_set_append_revisions_only(self):
2198
2113
        return True
2203
2118
    supports_reference_locations = True
2204
2119
 
2205
2120
 
2206
 
class BzrBranchFormat7(BranchFormatMetadir):
 
2121
class BzrBranchFormat7(BzrBranchFormat8):
2207
2122
    """Branch format with last-revision, tags, and a stacked location pointer.
2208
2123
 
2209
2124
    The stacked location pointer is passed down to the repository and requires
2212
2127
    This format was introduced in bzr 1.6.
2213
2128
    """
2214
2129
 
2215
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2216
 
                   append_revisions_only=None):
 
2130
    def initialize(self, a_bzrdir, name=None):
2217
2131
        """Create a branch of this format in a_bzrdir."""
2218
2132
        utf8_files = [('last-revision', '0 null:\n'),
2219
 
                      ('branch.conf',
2220
 
                          self._get_initial_config(append_revisions_only)),
 
2133
                      ('branch.conf', ''),
2221
2134
                      ('tags', ''),
2222
2135
                      ]
2223
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
2136
        return self._initialize_helper(a_bzrdir, utf8_files, name)
2224
2137
 
2225
2138
    def _branch_class(self):
2226
2139
        return BzrBranch7
2236
2149
    def supports_set_append_revisions_only(self):
2237
2150
        return True
2238
2151
 
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
 
 
2246
2152
    supports_reference_locations = False
2247
2153
 
2248
2154
 
2275
2181
        transport = a_bzrdir.get_branch_transport(None, name=name)
2276
2182
        location = transport.put_bytes('location', to_branch.base)
2277
2183
 
2278
 
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2279
 
            repository=None, append_revisions_only=None):
 
2184
    def initialize(self, a_bzrdir, name=None, target_branch=None):
2280
2185
        """Create a branch of this format in a_bzrdir."""
2281
2186
        if target_branch is None:
2282
2187
            # this format does not implement branch itself, thus the implicit
2283
2188
            # creation contract must see it as uninitializable
2284
2189
            raise errors.UninitializableFormat(self)
2285
2190
        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)
2288
2191
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2289
2192
        branch_transport.put_bytes('location',
2290
2193
            target_branch.bzrdir.user_url)
2312
2215
        return clone
2313
2216
 
2314
2217
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2315
 
             possible_transports=None, ignore_fallbacks=False,
2316
 
             found_repository=None):
 
2218
             possible_transports=None, ignore_fallbacks=False):
2317
2219
        """Return the branch that the branch reference in a_bzrdir points at.
2318
2220
 
2319
2221
        :param a_bzrdir: A BzrDir that contains a branch.
2350
2252
        return result
2351
2253
 
2352
2254
 
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
 
 
2367
2255
network_format_registry = registry.FormatRegistry()
2368
2256
"""Registry of formats indexed by their network name.
2369
2257
 
2372
2260
BranchFormat.network_name() for more detail.
2373
2261
"""
2374
2262
 
2375
 
format_registry = BranchFormatRegistry(network_format_registry)
2376
 
 
2377
2263
 
2378
2264
# formats which have no format string are not discoverable
2379
2265
# and not independently creatable, so are not registered.
2381
2267
__format6 = BzrBranchFormat6()
2382
2268
__format7 = BzrBranchFormat7()
2383
2269
__format8 = BzrBranchFormat8()
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)
 
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__)
2390
2280
 
2391
2281
 
2392
2282
class BranchWriteLockResult(LogicalLockResult):
2459
2349
    base = property(_get_base, doc="The URL for the root of this branch.")
2460
2350
 
2461
2351
    def _get_config(self):
2462
 
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
 
2352
        return TransportConfig(self._transport, 'branch.conf')
2463
2353
 
2464
2354
    def is_locked(self):
2465
2355
        return self.control_files.is_locked()
2540
2430
        """See Branch.print_file."""
2541
2431
        return self.repository.print_file(file, revision_id)
2542
2432
 
 
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
 
2543
2496
    @needs_write_lock
2544
2497
    def set_last_revision_info(self, revno, revision_id):
2545
 
        if not revision_id or not isinstance(revision_id, basestring):
2546
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
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
        """
2547
2508
        revision_id = _mod_revision.ensure_null(revision_id)
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)
 
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))
2556
2537
 
2557
2538
    def basis_tree(self):
2558
2539
        """See Branch.basis_tree."""
2567
2548
                pass
2568
2549
        return None
2569
2550
 
 
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
 
2570
2573
    def get_stacked_on_url(self):
2571
2574
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2572
2575
 
2583
2586
            self._transport.put_bytes('parent', url + '\n',
2584
2587
                mode=self.bzrdir._get_file_mode())
2585
2588
 
 
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
 
2586
2623
    @needs_write_lock
2587
 
    def unbind(self):
2588
 
        """If bound, unbind"""
2589
 
        return self.set_bound_location(None)
 
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
2590
2638
 
2591
2639
    @needs_write_lock
2592
2640
    def bind(self, other):
2614
2662
        # history around
2615
2663
        self.set_bound_location(other.base)
2616
2664
 
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
 
 
2645
2665
    @needs_write_lock
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
 
2666
    def unbind(self):
 
2667
        """If bound, unbind"""
 
2668
        return self.set_bound_location(None)
2661
2669
 
2662
2670
    @needs_write_lock
2663
2671
    def update(self, possible_transports=None):
2676
2684
            return old_tip
2677
2685
        return None
2678
2686
 
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):
 
2687
 
 
2688
class BzrBranch8(BzrBranch5):
2808
2689
    """A branch that stores tree-reference locations."""
2809
2690
 
2810
2691
    def _open_hook(self):
2836
2717
        self._last_revision_info_cache = None
2837
2718
        self._reference_info = None
2838
2719
 
 
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
 
2839
2765
    def _check_history_violation(self, revision_id):
2840
 
        current_revid = self.last_revision()
2841
 
        last_revision = _mod_revision.ensure_null(current_revid)
 
2766
        last_revision = _mod_revision.ensure_null(self.last_revision())
2842
2767
        if _mod_revision.is_null(last_revision):
2843
2768
            return
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)
 
2769
        if last_revision not in self._lefthand_history(revision_id):
 
2770
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
2849
2771
 
2850
2772
    def _gen_revision_history(self):
2851
2773
        """Generate the revision history from last revision
2854
2776
        self._extend_partial_history(stop_index=last_revno-1)
2855
2777
        return list(reversed(self._partial_revision_history_cache))
2856
2778
 
 
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
 
2857
2795
    @needs_write_lock
2858
2796
    def _set_parent_location(self, url):
2859
2797
        """Set the parent branch"""
2945
2883
 
2946
2884
    def set_bound_location(self, location):
2947
2885
        """See Branch.set_push_location."""
2948
 
        self._master_branch_cache = None
2949
2886
        result = None
2950
2887
        config = self.get_config()
2951
2888
        if location is None:
2982
2919
        # you can always ask for the URL; but you might not be able to use it
2983
2920
        # if the repo can't support stacking.
2984
2921
        ## self._check_stackable_repo()
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)
 
2922
        stacked_url = self._get_config_location('stacked_on_location')
2990
2923
        if stacked_url is None:
2991
2924
            raise errors.NotStacked(self)
2992
2925
        return stacked_url
2993
2926
 
 
2927
    def _get_append_revisions_only(self):
 
2928
        return self.get_config(
 
2929
            ).get_user_option_as_bool('append_revisions_only')
 
2930
 
 
2931
    @needs_write_lock
 
2932
    def generate_revision_history(self, revision_id, last_rev=None,
 
2933
                                  other_branch=None):
 
2934
        """See BzrBranch5.generate_revision_history"""
 
2935
        history = self._lefthand_history(revision_id, last_rev, other_branch)
 
2936
        revno = len(history)
 
2937
        self.set_last_revision_info(revno, revision_id)
 
2938
 
2994
2939
    @needs_read_lock
2995
2940
    def get_rev_id(self, revno, history=None):
2996
2941
        """Find the revision id of the specified revno."""
3020
2965
        try:
3021
2966
            index = self._partial_revision_history_cache.index(revision_id)
3022
2967
        except ValueError:
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)
 
2968
            self._extend_partial_history(stop_revision=revision_id)
3027
2969
            index = len(self._partial_revision_history_cache) - 1
3028
2970
            if self._partial_revision_history_cache[index] != revision_id:
3029
2971
                raise errors.NoSuchRevision(self, revision_id)
3082
3024
    :ivar local_branch: target branch if there is a Master, else None
3083
3025
    :ivar target_branch: Target/destination branch object. (write locked)
3084
3026
    :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
3086
3027
    """
3087
3028
 
3088
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3089
3029
    def __int__(self):
3090
 
        """Return the relative change in revno.
3091
 
 
3092
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3093
 
        """
 
3030
        # DEPRECATED: pull used to return the change in revno
3094
3031
        return self.new_revno - self.old_revno
3095
3032
 
3096
3033
    def report(self, to_file):
3097
 
        tag_conflicts = getattr(self, "tag_conflicts", None)
3098
 
        tag_updates = getattr(self, "tag_updates", None)
3099
3034
        if not is_quiet():
3100
 
            if self.old_revid != self.new_revid:
 
3035
            if self.old_revid == self.new_revid:
 
3036
                to_file.write('No revisions to pull.\n')
 
3037
            else:
3101
3038
                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')
3109
3039
        self._show_tag_conficts(to_file)
3110
3040
 
3111
3041
 
3128
3058
        target, otherwise it will be None.
3129
3059
    """
3130
3060
 
3131
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3132
3061
    def __int__(self):
3133
 
        """Return the relative change in revno.
3134
 
 
3135
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3136
 
        """
 
3062
        # DEPRECATED: push used to return the change in revno
3137
3063
        return self.new_revno - self.old_revno
3138
3064
 
3139
3065
    def report(self, to_file):
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.'))
 
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)
3156
3071
        self._show_tag_conficts(to_file)
3157
3072
 
3158
3073
 
3172
3087
        :param verbose: Requests more detailed display of what was checked,
3173
3088
            if any.
3174
3089
        """
3175
 
        note(gettext('checked branch {0} format {1}').format(
3176
 
                                self.branch.user_url, self.branch._format))
 
3090
        note('checked branch %s format %s', self.branch.user_url,
 
3091
            self.branch._format)
3177
3092
        for error in self.errors:
3178
 
            note(gettext('found error:%s'), error)
 
3093
            note('found error:%s', error)
3179
3094
 
3180
3095
 
3181
3096
class Converter5to6(object):
3220
3135
 
3221
3136
 
3222
3137
class Converter7to8(object):
3223
 
    """Perform an in-place upgrade of format 7 to format 8"""
 
3138
    """Perform an in-place upgrade of format 6 to format 7"""
3224
3139
 
3225
3140
    def convert(self, branch):
3226
3141
        format = BzrBranchFormat8()
3229
3144
        branch._transport.put_bytes('format', format.get_format_string())
3230
3145
 
3231
3146
 
 
3147
def _run_with_write_locked_target(target, callable, *args, **kwargs):
 
3148
    """Run ``callable(*args, **kwargs)``, write-locking target for the
 
3149
    duration.
 
3150
 
 
3151
    _run_with_write_locked_target will attempt to release the lock it acquires.
 
3152
 
 
3153
    If an exception is raised by callable, then that exception *will* be
 
3154
    propagated, even if the unlock attempt raises its own error.  Thus
 
3155
    _run_with_write_locked_target should be preferred to simply doing::
 
3156
 
 
3157
        target.lock_write()
 
3158
        try:
 
3159
            return callable(*args, **kwargs)
 
3160
        finally:
 
3161
            target.unlock()
 
3162
 
 
3163
    """
 
3164
    # This is very similar to bzrlib.decorators.needs_write_lock.  Perhaps they
 
3165
    # should share code?
 
3166
    target.lock_write()
 
3167
    try:
 
3168
        result = callable(*args, **kwargs)
 
3169
    except:
 
3170
        exc_info = sys.exc_info()
 
3171
        try:
 
3172
            target.unlock()
 
3173
        finally:
 
3174
            raise exc_info[0], exc_info[1], exc_info[2]
 
3175
    else:
 
3176
        target.unlock()
 
3177
        return result
 
3178
 
 
3179
 
3232
3180
class InterBranch(InterObject):
3233
3181
    """This class represents operations taking place between two branches.
3234
3182
 
3240
3188
    _optimisers = []
3241
3189
    """The available optimised InterBranch types."""
3242
3190
 
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)
 
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)
3252
3195
 
3253
 
    @needs_write_lock
3254
3196
    def pull(self, overwrite=False, stop_revision=None,
3255
3197
             possible_transports=None, local=False):
3256
3198
        """Mirror source into target branch.
3261
3203
        """
3262
3204
        raise NotImplementedError(self.pull)
3263
3205
 
3264
 
    @needs_write_lock
3265
 
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
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,
3266
3220
             _override_hook_source_branch=None):
3267
3221
        """Mirror the source branch into the target branch.
3268
3222
 
3270
3224
        """
3271
3225
        raise NotImplementedError(self.push)
3272
3226
 
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
 
 
3291
3227
 
3292
3228
class GenericInterBranch(InterBranch):
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, [])
 
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()."""
3334
3239
        self.source.lock_read()
3335
3240
        try:
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)
 
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)
3346
3276
        finally:
3347
3277
            self.source.unlock()
3348
3278
 
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
3389
3279
    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,
3515
3280
             possible_transports=None, _hook_master=None, run_hooks=True,
3516
 
             _override_hook_target=None, local=False,
3517
 
             merge_tags_to_master=True):
 
3281
             _override_hook_target=None, local=False):
3518
3282
        """See Branch.pull.
3519
3283
 
3520
 
        This function is the core worker, used by GenericInterBranch.pull to
3521
 
        avoid duplication when pulling source->master and source->local.
3522
 
 
3523
3284
        :param _hook_master: Private parameter - set the branch to
3524
3285
            be supplied as the master to pull hooks.
3525
3286
        :param run_hooks: Private parameter - if false, this branch
3526
3287
            is being called because it's the master of the primary branch,
3527
3288
            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.
3530
3289
        :param _override_hook_target: Private parameter - set the branch to be
3531
3290
            supplied as the target_branch to pull hooks.
3532
3291
        :param local: Only update the local branch, and not the bound branch.
3551
3310
            # -- JRV20090506
3552
3311
            result.old_revno, result.old_revid = \
3553
3312
                self.target.last_revision_info()
3554
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3555
 
                graph=graph)
 
3313
            self.target.update_revisions(self.source, stop_revision,
 
3314
                overwrite=overwrite, graph=graph)
3556
3315
            # TODO: The old revid should be specified when merging tags, 
3557
3316
            # so a tags implementation that versions tags can only 
3558
3317
            # pull in the most recent changes. -- JRV20090506
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))
 
3318
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3319
                overwrite)
3562
3320
            result.new_revno, result.new_revid = self.target.last_revision_info()
3563
3321
            if _hook_master:
3564
3322
                result.master_branch = _hook_master
3573
3331
            self.source.unlock()
3574
3332
        return result
3575
3333
 
 
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
 
3576
3442
 
3577
3443
InterBranch.register_optimiser(GenericInterBranch)
 
3444
InterBranch.register_optimiser(InterToBranch5)