~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Vincent Ladeuil
  • Date: 2009-05-05 15:31:34 UTC
  • mto: (4343.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4344.
  • Revision ID: v.ladeuil+lp@free.fr-20090505153134-q4bp4is9gywsmzrv
Clean up test for log formats.

* bzrlib/tests/blackbox/test_logformats.py:
Update tests to actual style.

Show diffs side-by-side

added added

removed removed

Lines of Context:
91
91
        self._revision_history_cache = None
92
92
        self._revision_id_to_revno_cache = None
93
93
        self._partial_revision_id_to_revno_cache = {}
94
 
        self._partial_revision_history_cache = []
95
94
        self._last_revision_info_cache = None
96
95
        self._merge_sorted_revisions_cache = None
97
96
        self._open_hook()
102
101
    def _open_hook(self):
103
102
        """Called by init to allow simpler extension of the base class."""
104
103
 
105
 
    def _activate_fallback_location(self, url):
 
104
    def _activate_fallback_location(self, url, lock_style):
106
105
        """Activate the branch/repository from url as a fallback repository."""
107
106
        repo = self._get_fallback_repository(url)
108
 
        if repo.has_same_location(self.repository):
109
 
            raise errors.UnstackableLocationError(self.base, url)
 
107
        if lock_style == 'write':
 
108
            repo.lock_write()
 
109
        elif lock_style == 'read':
 
110
            repo.lock_read()
110
111
        self.repository.add_fallback_repository(repo)
111
112
 
112
113
    def break_lock(self):
128
129
            raise errors.UnstackableRepositoryFormat(self.repository._format,
129
130
                self.repository.base)
130
131
 
131
 
    def _extend_partial_history(self, stop_index=None, stop_revision=None):
132
 
        """Extend the partial history to include a given index
133
 
 
134
 
        If a stop_index is supplied, stop when that index has been reached.
135
 
        If a stop_revision is supplied, stop when that revision is
136
 
        encountered.  Otherwise, stop when the beginning of history is
137
 
        reached.
138
 
 
139
 
        :param stop_index: The index which should be present.  When it is
140
 
            present, history extension will stop.
141
 
        :param stop_revision: The revision id which should be present.  When
142
 
            it is encountered, history extension will stop.
143
 
        """
144
 
        if len(self._partial_revision_history_cache) == 0:
145
 
            self._partial_revision_history_cache = [self.last_revision()]
146
 
        repository._iter_for_revno(
147
 
            self.repository, self._partial_revision_history_cache,
148
 
            stop_index=stop_index, stop_revision=stop_revision)
149
 
        if self._partial_revision_history_cache[-1] == _mod_revision.NULL_REVISION:
150
 
            self._partial_revision_history_cache.pop()
151
 
 
152
 
    def _get_check_refs(self):
153
 
        """Get the references needed for check().
154
 
 
155
 
        See bzrlib.check.
156
 
        """
157
 
        revid = self.last_revision()
158
 
        return [('revision-existence', revid), ('lefthand-distance', revid)]
159
 
 
160
132
    @staticmethod
161
133
    def open(base, _unsupported=False, possible_transports=None):
162
134
        """Open the branch rooted at base.
530
502
        """
531
503
        raise errors.UpgradeRequired(self.base)
532
504
 
533
 
    def set_append_revisions_only(self, enabled):
534
 
        if not self._format.supports_set_append_revisions_only():
535
 
            raise errors.UpgradeRequired(self.base)
536
 
        if enabled:
537
 
            value = 'True'
538
 
        else:
539
 
            value = 'False'
540
 
        self.get_config().set_user_option('append_revisions_only', value,
541
 
            warn_masked=True)
542
 
 
543
505
    def set_reference_info(self, file_id, tree_path, branch_location):
544
506
        """Set the branch location to use for a tree reference."""
545
507
        raise errors.UnsupportedOperation(self.set_reference_info, self)
671
633
        """
672
634
        if not self._format.supports_stacking():
673
635
            raise errors.UnstackableBranchFormat(self._format, self.base)
674
 
        # XXX: Changing from one fallback repository to another does not check
675
 
        # that all the data you need is present in the new fallback.
676
 
        # Possibly it should.
677
636
        self._check_stackable_repo()
678
637
        if not url:
679
638
            try:
681
640
            except (errors.NotStacked, errors.UnstackableBranchFormat,
682
641
                errors.UnstackableRepositoryFormat):
683
642
                return
684
 
            self._unstack()
 
643
            url = ''
 
644
            # XXX: Lock correctness - should unlock our old repo if we were
 
645
            # locked.
 
646
            # repositories don't offer an interface to remove fallback
 
647
            # repositories today; take the conceptually simpler option and just
 
648
            # reopen it.
 
649
            self.repository = self.bzrdir.find_repository()
 
650
            self.repository.lock_write()
 
651
            # for every revision reference the branch has, ensure it is pulled
 
652
            # in.
 
653
            source_repository = self._get_fallback_repository(old_url)
 
654
            for revision_id in chain([self.last_revision()],
 
655
                self.tags.get_reverse_tag_dict()):
 
656
                self.repository.fetch(source_repository, revision_id,
 
657
                    find_ghosts=True)
685
658
        else:
686
 
            self._activate_fallback_location(url)
 
659
            self._activate_fallback_location(url, 'write')
687
660
        # write this out after the repository is stacked to avoid setting a
688
661
        # stacked config that doesn't work.
689
662
        self._set_config_location('stacked_on_location', url)
690
663
 
691
 
    def _unstack(self):
692
 
        """Change a branch to be unstacked, copying data as needed.
693
 
        
694
 
        Don't call this directly, use set_stacked_on_url(None).
695
 
        """
696
 
        pb = ui.ui_factory.nested_progress_bar()
697
 
        try:
698
 
            pb.update("Unstacking")
699
 
            # The basic approach here is to fetch the tip of the branch,
700
 
            # including all available ghosts, from the existing stacked
701
 
            # repository into a new repository object without the fallbacks. 
702
 
            #
703
 
            # XXX: See <https://launchpad.net/bugs/397286> - this may not be
704
 
            # correct for CHKMap repostiories
705
 
            old_repository = self.repository
706
 
            if len(old_repository._fallback_repositories) != 1:
707
 
                raise AssertionError("can't cope with fallback repositories "
708
 
                    "of %r" % (self.repository,))
709
 
            # unlock it, including unlocking the fallback
710
 
            old_repository.unlock()
711
 
            old_repository.lock_read()
712
 
            try:
713
 
                # Repositories don't offer an interface to remove fallback
714
 
                # repositories today; take the conceptually simpler option and just
715
 
                # reopen it.  We reopen it starting from the URL so that we
716
 
                # get a separate connection for RemoteRepositories and can
717
 
                # stream from one of them to the other.  This does mean doing
718
 
                # separate SSH connection setup, but unstacking is not a
719
 
                # common operation so it's tolerable.
720
 
                new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
721
 
                new_repository = new_bzrdir.find_repository()
722
 
                self.repository = new_repository
723
 
                if self.repository._fallback_repositories:
724
 
                    raise AssertionError("didn't expect %r to have "
725
 
                        "fallback_repositories"
726
 
                        % (self.repository,))
727
 
                # this is not paired with an unlock because it's just restoring
728
 
                # the previous state; the lock's released when set_stacked_on_url
729
 
                # returns
730
 
                self.repository.lock_write()
731
 
                # XXX: If you unstack a branch while it has a working tree
732
 
                # with a pending merge, the pending-merged revisions will no
733
 
                # longer be present.  You can (probably) revert and remerge.
734
 
                #
735
 
                # XXX: This only fetches up to the tip of the repository; it
736
 
                # doesn't bring across any tags.  That's fairly consistent
737
 
                # with how branch works, but perhaps not ideal.
738
 
                self.repository.fetch(old_repository,
739
 
                    revision_id=self.last_revision(),
740
 
                    find_ghosts=True)
741
 
            finally:
742
 
                old_repository.unlock()
743
 
        finally:
744
 
            pb.finished()
745
664
 
746
665
    def _set_tags_bytes(self, bytes):
747
666
        """Mirror method for _get_tags_bytes.
783
702
        self._revision_id_to_revno_cache = None
784
703
        self._last_revision_info_cache = None
785
704
        self._merge_sorted_revisions_cache = None
786
 
        self._partial_revision_history_cache = []
787
 
        self._partial_revision_id_to_revno_cache = {}
788
705
 
789
706
    def _gen_revision_history(self):
790
707
        """Return sequence of revision hashes on to this branch.
829
746
        """Older format branches cannot bind or unbind."""
830
747
        raise errors.UpgradeRequired(self.base)
831
748
 
 
749
    def set_append_revisions_only(self, enabled):
 
750
        """Older format branches are never restricted to append-only"""
 
751
        raise errors.UpgradeRequired(self.base)
 
752
 
832
753
    def last_revision(self):
833
754
        """Return last revision id, or NULL_REVISION."""
834
755
        return self.last_revision_info()[1]
914
835
        except ValueError:
915
836
            raise errors.NoSuchRevision(self, revision_id)
916
837
 
917
 
    @needs_read_lock
918
838
    def get_rev_id(self, revno, history=None):
919
839
        """Find the revision id of the specified revno."""
920
840
        if revno == 0:
921
841
            return _mod_revision.NULL_REVISION
922
 
        last_revno, last_revid = self.last_revision_info()
923
 
        if revno == last_revno:
924
 
            return last_revid
925
 
        if revno <= 0 or revno > last_revno:
 
842
        if history is None:
 
843
            history = self.revision_history()
 
844
        if revno <= 0 or revno > len(history):
926
845
            raise errors.NoSuchRevision(self, revno)
927
 
        distance_from_last = last_revno - revno
928
 
        if len(self._partial_revision_history_cache) <= distance_from_last:
929
 
            self._extend_partial_history(distance_from_last)
930
 
        return self._partial_revision_history_cache[distance_from_last]
 
846
        return history[revno - 1]
931
847
 
932
 
    @needs_write_lock
933
848
    def pull(self, source, overwrite=False, stop_revision=None,
934
 
             possible_transports=None, *args, **kwargs):
 
849
             possible_transports=None, _override_hook_target=None):
935
850
        """Mirror source into this branch.
936
851
 
937
852
        This branch is considered to be 'local', having low latency.
938
853
 
939
854
        :returns: PullResult instance
940
855
        """
941
 
        return InterBranch.get(source, self).pull(overwrite=overwrite,
942
 
            stop_revision=stop_revision,
943
 
            possible_transports=possible_transports, *args, **kwargs)
 
856
        raise NotImplementedError(self.pull)
944
857
 
945
 
    def push(self, target, overwrite=False, stop_revision=None, *args,
946
 
        **kwargs):
 
858
    def push(self, target, overwrite=False, stop_revision=None):
947
859
        """Mirror this branch into target.
948
860
 
949
861
        This branch is considered to be 'local', having low latency.
950
862
        """
951
 
        return InterBranch.get(self, target).push(overwrite, stop_revision,
952
 
            *args, **kwargs)
953
 
 
954
 
    def lossy_push(self, target, stop_revision=None):
955
 
        """Push deltas into another branch.
956
 
 
957
 
        :note: This does not, like push, retain the revision ids from 
958
 
            the source branch and will, rather than adding bzr-specific 
959
 
            metadata, push only those semantics of the revision that can be 
960
 
            natively represented by this branch' VCS.
961
 
 
962
 
        :param target: Target branch
963
 
        :param stop_revision: Revision to push, defaults to last revision.
964
 
        :return: BranchPushResult with an extra member revidmap: 
965
 
            A dictionary mapping revision ids from the target branch 
966
 
            to new revision ids in the target branch, for each 
967
 
            revision that was pushed.
968
 
        """
969
 
        inter = InterBranch.get(self, target)
970
 
        lossy_push = getattr(inter, "lossy_push", None)
971
 
        if lossy_push is None:
972
 
            raise errors.LossyPushToSameVCS(self, target)
973
 
        return lossy_push(stop_revision)
 
863
        raise NotImplementedError(self.push)
974
864
 
975
865
    def basis_tree(self):
976
866
        """Return `Tree` object for last revision."""
1016
906
            location = None
1017
907
        return location
1018
908
 
1019
 
    def get_child_submit_format(self):
1020
 
        """Return the preferred format of submissions to this branch."""
1021
 
        return self.get_config().get_user_option("child_submit_format")
1022
 
 
1023
909
    def get_submit_branch(self):
1024
910
        """Return the submit location of the branch.
1025
911
 
1173
1059
        source_revno, source_revision_id = self.last_revision_info()
1174
1060
        if revision_id is None:
1175
1061
            revno, revision_id = source_revno, source_revision_id
 
1062
        elif source_revision_id == revision_id:
 
1063
            # we know the revno without needing to walk all of history
 
1064
            revno = source_revno
1176
1065
        else:
1177
 
            graph = self.repository.get_graph()
1178
 
            try:
1179
 
                revno = graph.find_distance_to_null(revision_id, 
1180
 
                    [(source_revision_id, source_revno)])
1181
 
            except errors.GhostRevisionsHaveNoRevno:
1182
 
                # Default to 1, if we can't find anything else
1183
 
                revno = 1
 
1066
            # To figure out the revno for a random revision, we need to build
 
1067
            # the revision history, and count its length.
 
1068
            # We don't care about the order, just how long it is.
 
1069
            # Alternatively, we could start at the current location, and count
 
1070
            # backwards. But there is no guarantee that we will find it since
 
1071
            # it may be a merged revision.
 
1072
            revno = len(list(self.repository.iter_reverse_revision_history(
 
1073
                                                                revision_id)))
1184
1074
        destination.set_last_revision_info(revno, revision_id)
1185
1075
 
1186
1076
    @needs_read_lock
1220
1110
        target._set_all_reference_info(target_reference_dict)
1221
1111
 
1222
1112
    @needs_read_lock
1223
 
    def check(self, refs):
 
1113
    def check(self):
1224
1114
        """Check consistency of the branch.
1225
1115
 
1226
1116
        In particular this checks that revisions given in the revision-history
1229
1119
 
1230
1120
        Callers will typically also want to check the repository.
1231
1121
 
1232
 
        :param refs: Calculated refs for this branch as specified by
1233
 
            branch._get_check_refs()
1234
1122
        :return: A BranchCheckResult.
1235
1123
        """
1236
 
        result = BranchCheckResult(self)
 
1124
        mainline_parent_id = None
1237
1125
        last_revno, last_revision_id = self.last_revision_info()
1238
 
        actual_revno = refs[('lefthand-distance', last_revision_id)]
1239
 
        if actual_revno != last_revno:
1240
 
            result.errors.append(errors.BzrCheckError(
1241
 
                'revno does not match len(mainline) %s != %s' % (
1242
 
                last_revno, actual_revno)))
1243
 
        # TODO: We should probably also check that self.revision_history
1244
 
        # matches the repository for older branch formats.
1245
 
        # If looking for the code that cross-checks repository parents against
1246
 
        # the iter_reverse_revision_history output, that is now a repository
1247
 
        # specific check.
1248
 
        return result
 
1126
        real_rev_history = list(self.repository.iter_reverse_revision_history(
 
1127
                                last_revision_id))
 
1128
        real_rev_history.reverse()
 
1129
        if len(real_rev_history) != last_revno:
 
1130
            raise errors.BzrCheckError('revno does not match len(mainline)'
 
1131
                ' %s != %s' % (last_revno, len(real_rev_history)))
 
1132
        # TODO: We should probably also check that real_rev_history actually
 
1133
        #       matches self.revision_history()
 
1134
        for revision_id in real_rev_history:
 
1135
            try:
 
1136
                revision = self.repository.get_revision(revision_id)
 
1137
            except errors.NoSuchRevision, e:
 
1138
                raise errors.BzrCheckError("mainline revision {%s} not in repository"
 
1139
                            % revision_id)
 
1140
            # In general the first entry on the revision history has no parents.
 
1141
            # But it's not illegal for it to have parents listed; this can happen
 
1142
            # in imports from Arch when the parents weren't reachable.
 
1143
            if mainline_parent_id is not None:
 
1144
                if mainline_parent_id not in revision.parent_ids:
 
1145
                    raise errors.BzrCheckError("previous revision {%s} not listed among "
 
1146
                                        "parents of {%s}"
 
1147
                                        % (mainline_parent_id, revision_id))
 
1148
            mainline_parent_id = revision_id
 
1149
        return BranchCheckResult(self)
1249
1150
 
1250
1151
    def _get_checkout_format(self):
1251
1152
        """Return the most suitable metadir for a checkout of this branch.
1418
1319
    _formats = {}
1419
1320
    """The known formats."""
1420
1321
 
1421
 
    can_set_append_revisions_only = True
1422
 
 
1423
1322
    def __eq__(self, other):
1424
1323
        return self.__class__ is other.__class__
1425
1324
 
1578
1477
    def set_default_format(klass, format):
1579
1478
        klass._default_format = format
1580
1479
 
1581
 
    def supports_set_append_revisions_only(self):
1582
 
        """True if this format supports set_append_revisions_only."""
1583
 
        return False
1584
 
 
1585
1480
    def supports_stacking(self):
1586
1481
        """True if this format records a stacked-on branch."""
1587
1482
        return False
1869
1764
        """See bzrlib.branch.BranchFormat.make_tags()."""
1870
1765
        return BasicTags(branch)
1871
1766
 
1872
 
    def supports_set_append_revisions_only(self):
1873
 
        return True
1874
1767
 
1875
1768
 
1876
1769
class BzrBranchFormat8(BranchFormatMetadir):
1905
1798
        """See bzrlib.branch.BranchFormat.make_tags()."""
1906
1799
        return BasicTags(branch)
1907
1800
 
1908
 
    def supports_set_append_revisions_only(self):
1909
 
        return True
1910
 
 
1911
1801
    def supports_stacking(self):
1912
1802
        return True
1913
1803
 
1942
1832
        """See BranchFormat.get_format_description()."""
1943
1833
        return "Branch format 7"
1944
1834
 
1945
 
    def supports_set_append_revisions_only(self):
1946
 
        return True
1947
 
 
1948
1835
    supports_reference_locations = False
1949
1836
 
1950
1837
 
2288
2175
        """See Branch.basis_tree."""
2289
2176
        return self.repository.revision_tree(self.last_revision())
2290
2177
 
 
2178
    @needs_write_lock
 
2179
    def pull(self, source, overwrite=False, stop_revision=None,
 
2180
             _hook_master=None, run_hooks=True, possible_transports=None,
 
2181
             _override_hook_target=None):
 
2182
        """See Branch.pull.
 
2183
 
 
2184
        :param _hook_master: Private parameter - set the branch to
 
2185
            be supplied as the master to pull hooks.
 
2186
        :param run_hooks: Private parameter - if false, this branch
 
2187
            is being called because it's the master of the primary branch,
 
2188
            so it should not run its hooks.
 
2189
        :param _override_hook_target: Private parameter - set the branch to be
 
2190
            supplied as the target_branch to pull hooks.
 
2191
        """
 
2192
        result = PullResult()
 
2193
        result.source_branch = source
 
2194
        if _override_hook_target is None:
 
2195
            result.target_branch = self
 
2196
        else:
 
2197
            result.target_branch = _override_hook_target
 
2198
        source.lock_read()
 
2199
        try:
 
2200
            # We assume that during 'pull' the local repository is closer than
 
2201
            # the remote one.
 
2202
            source.update_references(self)
 
2203
            graph = self.repository.get_graph(source.repository)
 
2204
            result.old_revno, result.old_revid = self.last_revision_info()
 
2205
            self.update_revisions(source, stop_revision, overwrite=overwrite,
 
2206
                                  graph=graph)
 
2207
            result.tag_conflicts = source.tags.merge_to(self.tags, overwrite)
 
2208
            result.new_revno, result.new_revid = self.last_revision_info()
 
2209
            if _hook_master:
 
2210
                result.master_branch = _hook_master
 
2211
                result.local_branch = result.target_branch
 
2212
            else:
 
2213
                result.master_branch = result.target_branch
 
2214
                result.local_branch = None
 
2215
            if run_hooks:
 
2216
                for hook in Branch.hooks['post_pull']:
 
2217
                    hook(result)
 
2218
        finally:
 
2219
            source.unlock()
 
2220
        return result
 
2221
 
2291
2222
    def _get_parent_location(self):
2292
2223
        _locs = ['parent', 'pull', 'x-pull']
2293
2224
        for l in _locs:
2297
2228
                pass
2298
2229
        return None
2299
2230
 
 
2231
    @needs_read_lock
 
2232
    def push(self, target, overwrite=False, stop_revision=None,
 
2233
             _override_hook_source_branch=None):
 
2234
        """See Branch.push.
 
2235
 
 
2236
        This is the basic concrete implementation of push()
 
2237
 
 
2238
        :param _override_hook_source_branch: If specified, run
 
2239
        the hooks passing this Branch as the source, rather than self.
 
2240
        This is for use of RemoteBranch, where push is delegated to the
 
2241
        underlying vfs-based Branch.
 
2242
        """
 
2243
        # TODO: Public option to disable running hooks - should be trivial but
 
2244
        # needs tests.
 
2245
        return _run_with_write_locked_target(
 
2246
            target, self._push_with_bound_branches, target, overwrite,
 
2247
            stop_revision,
 
2248
            _override_hook_source_branch=_override_hook_source_branch)
 
2249
 
 
2250
    def _push_with_bound_branches(self, target, overwrite,
 
2251
            stop_revision,
 
2252
            _override_hook_source_branch=None):
 
2253
        """Push from self into target, and into target's master if any.
 
2254
 
 
2255
        This is on the base BzrBranch class even though it doesn't support
 
2256
        bound branches because the *target* might be bound.
 
2257
        """
 
2258
        def _run_hooks():
 
2259
            if _override_hook_source_branch:
 
2260
                result.source_branch = _override_hook_source_branch
 
2261
            for hook in Branch.hooks['post_push']:
 
2262
                hook(result)
 
2263
 
 
2264
        bound_location = target.get_bound_location()
 
2265
        if bound_location and target.base != bound_location:
 
2266
            # there is a master branch.
 
2267
            #
 
2268
            # XXX: Why the second check?  Is it even supported for a branch to
 
2269
            # be bound to itself? -- mbp 20070507
 
2270
            master_branch = target.get_master_branch()
 
2271
            master_branch.lock_write()
 
2272
            try:
 
2273
                # push into the master from this branch.
 
2274
                self._basic_push(master_branch, overwrite, stop_revision)
 
2275
                # and push into the target branch from this. Note that we push from
 
2276
                # this branch again, because its considered the highest bandwidth
 
2277
                # repository.
 
2278
                result = self._basic_push(target, overwrite, stop_revision)
 
2279
                result.master_branch = master_branch
 
2280
                result.local_branch = target
 
2281
                _run_hooks()
 
2282
                return result
 
2283
            finally:
 
2284
                master_branch.unlock()
 
2285
        else:
 
2286
            # no master branch
 
2287
            result = self._basic_push(target, overwrite, stop_revision)
 
2288
            # TODO: Why set master_branch and local_branch if there's no
 
2289
            # binding?  Maybe cleaner to just leave them unset? -- mbp
 
2290
            # 20070504
 
2291
            result.master_branch = target
 
2292
            result.local_branch = None
 
2293
            _run_hooks()
 
2294
            return result
 
2295
 
2300
2296
    def _basic_push(self, target, overwrite, stop_revision):
2301
2297
        """Basic implementation of push without bound branches or hooks.
2302
2298
 
2303
 
        Must be called with source read locked and target write locked.
 
2299
        Must be called with self read locked and target write locked.
2304
2300
        """
2305
2301
        result = BranchPushResult()
2306
2302
        result.source_branch = self
2311
2307
            # We assume that during 'push' this repository is closer than
2312
2308
            # the target.
2313
2309
            graph = self.repository.get_graph(target.repository)
2314
 
            target.update_revisions(self, stop_revision,
2315
 
                overwrite=overwrite, graph=graph)
 
2310
            target.update_revisions(self, stop_revision, overwrite=overwrite,
 
2311
                                    graph=graph)
2316
2312
        if self._push_should_merge_tags():
2317
 
            result.tag_conflicts = self.tags.merge_to(target.tags,
2318
 
                overwrite)
 
2313
            result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
2319
2314
        result.new_revno, result.new_revid = target.last_revision_info()
2320
2315
        return result
2321
2316
 
2342
2337
    It has support for a master_branch which is the data for bound branches.
2343
2338
    """
2344
2339
 
 
2340
    @needs_write_lock
 
2341
    def pull(self, source, overwrite=False, stop_revision=None,
 
2342
             run_hooks=True, possible_transports=None,
 
2343
             _override_hook_target=None):
 
2344
        """Pull from source into self, updating my master if any.
 
2345
 
 
2346
        :param run_hooks: Private parameter - if false, this branch
 
2347
            is being called because it's the master of the primary branch,
 
2348
            so it should not run its hooks.
 
2349
        """
 
2350
        bound_location = self.get_bound_location()
 
2351
        master_branch = None
 
2352
        if bound_location and source.base != bound_location:
 
2353
            # not pulling from master, so we need to update master.
 
2354
            master_branch = self.get_master_branch(possible_transports)
 
2355
            master_branch.lock_write()
 
2356
        try:
 
2357
            if master_branch:
 
2358
                # pull from source into master.
 
2359
                master_branch.pull(source, overwrite, stop_revision,
 
2360
                    run_hooks=False)
 
2361
            return super(BzrBranch5, self).pull(source, overwrite,
 
2362
                stop_revision, _hook_master=master_branch,
 
2363
                run_hooks=run_hooks,
 
2364
                _override_hook_target=_override_hook_target)
 
2365
        finally:
 
2366
            if master_branch:
 
2367
                master_branch.unlock()
 
2368
 
2345
2369
    def get_bound_location(self):
2346
2370
        try:
2347
2371
            return self._transport.get_bytes('bound')[:-1]
2453
2477
                    raise AssertionError(
2454
2478
                        "'transform_fallback_location' hook %s returned "
2455
2479
                        "None, not a URL." % hook_name)
2456
 
            self._activate_fallback_location(url)
 
2480
            self._activate_fallback_location(url, None)
2457
2481
 
2458
2482
    def __init__(self, *args, **kwargs):
2459
2483
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2460
2484
        super(BzrBranch8, self).__init__(*args, **kwargs)
2461
2485
        self._last_revision_info_cache = None
 
2486
        self._partial_revision_history_cache = []
2462
2487
        self._reference_info = None
2463
2488
 
2464
2489
    def _clear_cached_state(self):
2465
2490
        super(BzrBranch8, self)._clear_cached_state()
2466
2491
        self._last_revision_info_cache = None
 
2492
        self._partial_revision_history_cache = []
2467
2493
        self._reference_info = None
2468
2494
 
2469
2495
    def _last_revision_info(self):
2525
2551
        self._extend_partial_history(stop_index=last_revno-1)
2526
2552
        return list(reversed(self._partial_revision_history_cache))
2527
2553
 
 
2554
    def _extend_partial_history(self, stop_index=None, stop_revision=None):
 
2555
        """Extend the partial history to include a given index
 
2556
 
 
2557
        If a stop_index is supplied, stop when that index has been reached.
 
2558
        If a stop_revision is supplied, stop when that revision is
 
2559
        encountered.  Otherwise, stop when the beginning of history is
 
2560
        reached.
 
2561
 
 
2562
        :param stop_index: The index which should be present.  When it is
 
2563
            present, history extension will stop.
 
2564
        :param revision_id: The revision id which should be present.  When
 
2565
            it is encountered, history extension will stop.
 
2566
        """
 
2567
        repo = self.repository
 
2568
        if len(self._partial_revision_history_cache) == 0:
 
2569
            iterator = repo.iter_reverse_revision_history(self.last_revision())
 
2570
        else:
 
2571
            start_revision = self._partial_revision_history_cache[-1]
 
2572
            iterator = repo.iter_reverse_revision_history(start_revision)
 
2573
            #skip the last revision in the list
 
2574
            next_revision = iterator.next()
 
2575
        for revision_id in iterator:
 
2576
            self._partial_revision_history_cache.append(revision_id)
 
2577
            if (stop_index is not None and
 
2578
                len(self._partial_revision_history_cache) > stop_index):
 
2579
                break
 
2580
            if revision_id == stop_revision:
 
2581
                break
 
2582
 
2528
2583
    def _write_revision_history(self, history):
2529
2584
        """Factored out of set_revision_history.
2530
2585
 
2673
2728
            raise errors.NotStacked(self)
2674
2729
        return stacked_url
2675
2730
 
 
2731
    def set_append_revisions_only(self, enabled):
 
2732
        if enabled:
 
2733
            value = 'True'
 
2734
        else:
 
2735
            value = 'False'
 
2736
        self.get_config().set_user_option('append_revisions_only', value,
 
2737
            warn_masked=True)
 
2738
 
2676
2739
    def _get_append_revisions_only(self):
2677
2740
        value = self.get_config().get_user_option('append_revisions_only')
2678
2741
        return value == 'True'
2828
2891
 
2829
2892
    def __init__(self, branch):
2830
2893
        self.branch = branch
2831
 
        self.errors = []
2832
2894
 
2833
2895
    def report_results(self, verbose):
2834
2896
        """Report the check results via trace.note.
2836
2898
        :param verbose: Requests more detailed display of what was checked,
2837
2899
            if any.
2838
2900
        """
2839
 
        note('checked branch %s format %s', self.branch.base,
2840
 
            self.branch._format)
2841
 
        for error in self.errors:
2842
 
            note('found error:%s', error)
 
2901
        note('checked branch %s format %s',
 
2902
             self.branch.base,
 
2903
             self.branch._format)
2843
2904
 
2844
2905
 
2845
2906
class Converter5to6(object):
2940
3001
    @staticmethod
2941
3002
    def _get_branch_formats_to_test():
2942
3003
        """Return a tuple with the Branch formats to use when testing."""
2943
 
        raise NotImplementedError(InterBranch._get_branch_formats_to_test)
2944
 
 
2945
 
    def pull(self, overwrite=False, stop_revision=None,
2946
 
             possible_transports=None, local=False):
2947
 
        """Mirror source into target branch.
2948
 
 
2949
 
        The target branch is considered to be 'local', having low latency.
2950
 
 
2951
 
        :returns: PullResult instance
2952
 
        """
2953
 
        raise NotImplementedError(self.pull)
 
3004
        raise NotImplementedError(self._get_branch_formats_to_test)
2954
3005
 
2955
3006
    def update_revisions(self, stop_revision=None, overwrite=False,
2956
3007
                         graph=None):
2965
3016
        """
2966
3017
        raise NotImplementedError(self.update_revisions)
2967
3018
 
2968
 
    def push(self, overwrite=False, stop_revision=None,
2969
 
             _override_hook_source_branch=None):
2970
 
        """Mirror the source branch into the target branch.
2971
 
 
2972
 
        The source branch is considered to be 'local', having low latency.
2973
 
        """
2974
 
        raise NotImplementedError(self.push)
2975
 
 
2976
3019
 
2977
3020
class GenericInterBranch(InterBranch):
2978
3021
    """InterBranch implementation that uses public Branch functions.
3025
3068
        finally:
3026
3069
            self.source.unlock()
3027
3070
 
3028
 
    def pull(self, overwrite=False, stop_revision=None,
3029
 
             possible_transports=None, _hook_master=None, run_hooks=True,
3030
 
             _override_hook_target=None, local=False):
3031
 
        """See Branch.pull.
3032
 
 
3033
 
        :param _hook_master: Private parameter - set the branch to
3034
 
            be supplied as the master to pull hooks.
3035
 
        :param run_hooks: Private parameter - if false, this branch
3036
 
            is being called because it's the master of the primary branch,
3037
 
            so it should not run its hooks.
3038
 
        :param _override_hook_target: Private parameter - set the branch to be
3039
 
            supplied as the target_branch to pull hooks.
3040
 
        :param local: Only update the local branch, and not the bound branch.
3041
 
        """
3042
 
        # This type of branch can't be bound.
3043
 
        if local:
3044
 
            raise errors.LocalRequiresBoundBranch()
3045
 
        result = PullResult()
3046
 
        result.source_branch = self.source
3047
 
        if _override_hook_target is None:
3048
 
            result.target_branch = self.target
3049
 
        else:
3050
 
            result.target_branch = _override_hook_target
3051
 
        self.source.lock_read()
3052
 
        try:
3053
 
            # We assume that during 'pull' the target repository is closer than
3054
 
            # the source one.
3055
 
            self.source.update_references(self.target)
3056
 
            graph = self.target.repository.get_graph(self.source.repository)
3057
 
            # TODO: Branch formats should have a flag that indicates 
3058
 
            # that revno's are expensive, and pull() should honor that flag.
3059
 
            # -- JRV20090506
3060
 
            result.old_revno, result.old_revid = \
3061
 
                self.target.last_revision_info()
3062
 
            self.target.update_revisions(self.source, stop_revision,
3063
 
                overwrite=overwrite, graph=graph)
3064
 
            # TODO: The old revid should be specified when merging tags, 
3065
 
            # so a tags implementation that versions tags can only 
3066
 
            # pull in the most recent changes. -- JRV20090506
3067
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3068
 
                overwrite)
3069
 
            result.new_revno, result.new_revid = self.target.last_revision_info()
3070
 
            if _hook_master:
3071
 
                result.master_branch = _hook_master
3072
 
                result.local_branch = result.target_branch
3073
 
            else:
3074
 
                result.master_branch = result.target_branch
3075
 
                result.local_branch = None
3076
 
            if run_hooks:
3077
 
                for hook in Branch.hooks['post_pull']:
3078
 
                    hook(result)
3079
 
        finally:
3080
 
            self.source.unlock()
3081
 
        return result
3082
 
 
3083
 
    def push(self, overwrite=False, stop_revision=None,
3084
 
             _override_hook_source_branch=None):
3085
 
        """See InterBranch.push.
3086
 
 
3087
 
        This is the basic concrete implementation of push()
3088
 
 
3089
 
        :param _override_hook_source_branch: If specified, run
3090
 
        the hooks passing this Branch as the source, rather than self.
3091
 
        This is for use of RemoteBranch, where push is delegated to the
3092
 
        underlying vfs-based Branch.
3093
 
        """
3094
 
        # TODO: Public option to disable running hooks - should be trivial but
3095
 
        # needs tests.
3096
 
        self.source.lock_read()
3097
 
        try:
3098
 
            return _run_with_write_locked_target(
3099
 
                self.target, self._push_with_bound_branches, overwrite,
3100
 
                stop_revision,
3101
 
                _override_hook_source_branch=_override_hook_source_branch)
3102
 
        finally:
3103
 
            self.source.unlock()
3104
 
 
3105
 
    def _push_with_bound_branches(self, overwrite, stop_revision,
3106
 
            _override_hook_source_branch=None):
3107
 
        """Push from source into target, and into target's master if any.
3108
 
        """
3109
 
        def _run_hooks():
3110
 
            if _override_hook_source_branch:
3111
 
                result.source_branch = _override_hook_source_branch
3112
 
            for hook in Branch.hooks['post_push']:
3113
 
                hook(result)
3114
 
 
3115
 
        bound_location = self.target.get_bound_location()
3116
 
        if bound_location and self.target.base != bound_location:
3117
 
            # there is a master branch.
3118
 
            #
3119
 
            # XXX: Why the second check?  Is it even supported for a branch to
3120
 
            # be bound to itself? -- mbp 20070507
3121
 
            master_branch = self.target.get_master_branch()
3122
 
            master_branch.lock_write()
3123
 
            try:
3124
 
                # push into the master from the source branch.
3125
 
                self.source._basic_push(master_branch, overwrite, stop_revision)
3126
 
                # and push into the target branch from the source. Note that we
3127
 
                # push from the source branch again, because its considered the
3128
 
                # highest bandwidth repository.
3129
 
                result = self.source._basic_push(self.target, overwrite,
3130
 
                    stop_revision)
3131
 
                result.master_branch = master_branch
3132
 
                result.local_branch = self.target
3133
 
                _run_hooks()
3134
 
                return result
3135
 
            finally:
3136
 
                master_branch.unlock()
3137
 
        else:
3138
 
            # no master branch
3139
 
            result = self.source._basic_push(self.target, overwrite,
3140
 
                stop_revision)
3141
 
            # TODO: Why set master_branch and local_branch if there's no
3142
 
            # binding?  Maybe cleaner to just leave them unset? -- mbp
3143
 
            # 20070504
3144
 
            result.master_branch = self.target
3145
 
            result.local_branch = None
3146
 
            _run_hooks()
3147
 
            return result
3148
 
 
3149
3071
    @classmethod
3150
3072
    def is_compatible(self, source, target):
3151
3073
        # GenericBranch uses the public API, so always compatible
3152
3074
        return True
3153
3075
 
3154
3076
 
3155
 
class InterToBranch5(GenericInterBranch):
3156
 
 
3157
 
    @staticmethod
3158
 
    def _get_branch_formats_to_test():
3159
 
        return BranchFormat._default_format, BzrBranchFormat5()
3160
 
 
3161
 
    def pull(self, overwrite=False, stop_revision=None,
3162
 
             possible_transports=None, run_hooks=True,
3163
 
             _override_hook_target=None, local=False):
3164
 
        """Pull from source into self, updating my master if any.
3165
 
 
3166
 
        :param run_hooks: Private parameter - if false, this branch
3167
 
            is being called because it's the master of the primary branch,
3168
 
            so it should not run its hooks.
3169
 
        """
3170
 
        bound_location = self.target.get_bound_location()
3171
 
        if local and not bound_location:
3172
 
            raise errors.LocalRequiresBoundBranch()
3173
 
        master_branch = None
3174
 
        if not local and bound_location and self.source.base != bound_location:
3175
 
            # not pulling from master, so we need to update master.
3176
 
            master_branch = self.target.get_master_branch(possible_transports)
3177
 
            master_branch.lock_write()
3178
 
        try:
3179
 
            if master_branch:
3180
 
                # pull from source into master.
3181
 
                master_branch.pull(self.source, overwrite, stop_revision,
3182
 
                    run_hooks=False)
3183
 
            return super(InterToBranch5, self).pull(overwrite,
3184
 
                stop_revision, _hook_master=master_branch,
3185
 
                run_hooks=run_hooks,
3186
 
                _override_hook_target=_override_hook_target)
3187
 
        finally:
3188
 
            if master_branch:
3189
 
                master_branch.unlock()
3190
 
 
3191
 
 
3192
3077
InterBranch.register_optimiser(GenericInterBranch)
3193
 
InterBranch.register_optimiser(InterToBranch5)