/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to breezy/branch.py

  • Committer: Jelmer Vernooij
  • Date: 2018-11-17 00:47:52 UTC
  • mfrom: (7182 work)
  • mto: This revision was merged to the branch mainline in revision 7305.
  • Revision ID: jelmer@jelmer.uk-20181117004752-6ywampe5pfywlby4
Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
from __future__ import absolute_import
18
18
 
19
 
import sys
20
 
 
21
 
from . import errors
22
 
 
23
19
from .lazy_import import lazy_import
24
20
lazy_import(globals(), """
25
21
import itertools
26
22
from breezy import (
27
 
    cleanup,
28
23
    config as _mod_config,
29
24
    debug,
30
 
    fetch,
31
25
    memorytree,
32
26
    repository,
33
27
    revision as _mod_revision,
37
31
    urlutils,
38
32
    )
39
33
from breezy.bzr import (
 
34
    fetch,
40
35
    remote,
41
36
    vf_search,
42
37
    )
45
40
 
46
41
from . import (
47
42
    controldir,
 
43
    errors,
48
44
    registry,
49
45
    )
50
 
from .decorators import (
51
 
    only_raises,
52
 
    )
53
46
from .hooks import Hooks
54
47
from .inter import InterObject
55
48
from .lock import LogicalLockResult
56
49
from .sixish import (
57
 
    BytesIO,
58
50
    text_type,
59
51
    viewitems,
60
52
    )
64
56
class UnstackableBranchFormat(errors.BzrError):
65
57
 
66
58
    _fmt = ("The branch '%(url)s'(%(format)s) is not a stackable format. "
67
 
        "You will need to upgrade the branch to permit branch stacking.")
 
59
            "You will need to upgrade the branch to permit branch stacking.")
68
60
 
69
61
    def __init__(self, format, url):
70
62
        errors.BzrError.__init__(self)
162
154
        repository._iter_for_revno(
163
155
            self.repository, self._partial_revision_history_cache,
164
156
            stop_index=stop_index, stop_revision=stop_revision)
165
 
        if self._partial_revision_history_cache[-1] == _mod_revision.NULL_REVISION:
 
157
        if self._partial_revision_history_cache[-1] == \
 
158
                _mod_revision.NULL_REVISION:
166
159
            self._partial_revision_history_cache.pop()
167
160
 
168
161
    def _get_check_refs(self):
180
173
        For instance, if the branch is at URL/.bzr/branch,
181
174
        Branch.open(URL) -> a Branch instance.
182
175
        """
183
 
        control = controldir.ControlDir.open(base,
184
 
            possible_transports=possible_transports, _unsupported=_unsupported)
185
 
        return control.open_branch(unsupported=_unsupported,
 
176
        control = controldir.ControlDir.open(
 
177
            base, possible_transports=possible_transports,
 
178
            _unsupported=_unsupported)
 
179
        return control.open_branch(
 
180
            unsupported=_unsupported,
186
181
            possible_transports=possible_transports)
187
182
 
188
183
    @staticmethod
189
184
    def open_from_transport(transport, name=None, _unsupported=False,
190
 
            possible_transports=None):
 
185
                            possible_transports=None):
191
186
        """Open the branch rooted at transport"""
192
 
        control = controldir.ControlDir.open_from_transport(transport, _unsupported)
193
 
        return control.open_branch(name=name, unsupported=_unsupported,
 
187
        control = controldir.ControlDir.open_from_transport(
 
188
            transport, _unsupported)
 
189
        return control.open_branch(
 
190
            name=name, unsupported=_unsupported,
194
191
            possible_transports=possible_transports)
195
192
 
196
193
    @staticmethod
201
198
 
202
199
        Basically we keep looking up until we find the control directory or
203
200
        run into the root.  If there isn't one, raises NotBranchError.
204
 
        If there is one and it is either an unrecognised format or an unsupported
205
 
        format, UnknownFormatError or UnsupportedFormatError are raised.
206
 
        If there is one, it is returned, along with the unused portion of url.
 
201
        If there is one and it is either an unrecognised format or an
 
202
        unsupported format, UnknownFormatError or UnsupportedFormatError are
 
203
        raised.  If there is one, it is returned, along with the unused portion
 
204
        of url.
207
205
        """
208
 
        control, relpath = controldir.ControlDir.open_containing(url,
209
 
                                                         possible_transports)
 
206
        control, relpath = controldir.ControlDir.open_containing(
 
207
            url, possible_transports)
210
208
        branch = control.open_branch(possible_transports=possible_transports)
211
209
        return (branch, relpath)
212
210
 
278
276
                # Silently fall back to local implicit nick if the master is
279
277
                # unavailable
280
278
                mutter("Could not connect to bound branch, "
281
 
                    "falling back to local nick.\n " + str(e))
 
279
                       "falling back to local nick.\n " + str(e))
282
280
        return config.get_nickname()
283
281
 
284
282
    def _set_nick(self, nick):
307
305
        new_history = []
308
306
        check_not_reserved_id = _mod_revision.check_not_reserved_id
309
307
        # Do not include ghosts or graph origin in revision_history
310
 
        while (current_rev_id in parents_map and
311
 
               len(parents_map[current_rev_id]) > 0):
 
308
        while (current_rev_id in parents_map
 
309
               and len(parents_map[current_rev_id]) > 0):
312
310
            check_not_reserved_id(current_rev_id)
313
311
            new_history.append(current_rev_id)
314
312
            current_rev_id = parents_map[current_rev_id][0]
415
413
            This dictionary should not be modified by the caller.
416
414
        """
417
415
        if 'evil' in debug.debug_flags:
418
 
            mutter_callsite(3, "get_revision_id_to_revno_map scales with ancestry.")
 
416
            mutter_callsite(
 
417
                3, "get_revision_id_to_revno_map scales with ancestry.")
419
418
        with self.lock_read():
420
419
            if self._revision_id_to_revno_cache is not None:
421
420
                mapping = self._revision_id_to_revno_cache
422
421
            else:
423
422
                mapping = self._gen_revno_map()
424
423
                self._cache_revision_id_to_revno(mapping)
425
 
            # TODO: jam 20070417 Since this is being cached, should we be returning
426
 
            #       a copy?
427
 
            # I would rather not, and instead just declare that users should not
428
 
            # modify the return value.
 
424
            # TODO: jam 20070417 Since this is being cached, should we be
 
425
            # returning a copy?
 
426
            # I would rather not, and instead just declare that users should
 
427
            # not modify the return value.
429
428
            return mapping
430
429
 
431
430
    def _gen_revno_map(self):
438
437
 
439
438
        :return: A dictionary mapping revision_id => dotted revno.
440
439
        """
441
 
        revision_id_to_revno = dict((rev_id, revno)
442
 
            for rev_id, depth, revno, end_of_merge
443
 
             in self.iter_merge_sorted_revisions())
 
440
        revision_id_to_revno = {
 
441
            rev_id: revno for rev_id, depth, revno, end_of_merge
 
442
            in self.iter_merge_sorted_revisions()}
444
443
        return revision_id_to_revno
445
444
 
446
445
    def iter_merge_sorted_revisions(self, start_revision_id=None,
447
 
            stop_revision_id=None, stop_rule='exclude', direction='reverse'):
 
446
                                    stop_revision_id=None,
 
447
                                    stop_rule='exclude', direction='reverse'):
448
448
        """Walk the revisions for a branch in merge sorted order.
449
449
 
450
450
        Merge sorted order is the output from a merge-aware,
462
462
            * 'include' - the stop revision is the last item in the result
463
463
            * 'with-merges' - include the stop revision and all of its
464
464
              merged revisions in the result
465
 
            * 'with-merges-without-common-ancestry' - filter out revisions 
 
465
            * 'with-merges-without-common-ancestry' - filter out revisions
466
466
              that are in both ancestries
467
467
        :param direction: either 'reverse' or 'forward':
468
468
 
511
511
                raise ValueError('invalid direction %r' % direction)
512
512
 
513
513
    def _filter_merge_sorted_revisions(self, merge_sorted_revisions,
514
 
        start_revision_id, stop_revision_id, stop_rule):
 
514
                                       start_revision_id, stop_revision_id,
 
515
                                       stop_rule):
515
516
        """Iterate over an inclusive range of sorted revisions."""
516
517
        rev_iter = iter(merge_sorted_revisions)
517
518
        if start_revision_id is not None:
572
573
                if rev_id == left_parent:
573
574
                    # reached the left parent after the stop_revision
574
575
                    return
575
 
                if (not reached_stop_revision_id or
576
 
                        rev_id in revision_id_whitelist):
 
576
                if (not reached_stop_revision_id
 
577
                        or rev_id in revision_id_whitelist):
577
578
                    yield (rev_id, node.merge_depth, node.revno,
578
 
                       node.end_of_merge)
 
579
                           node.end_of_merge)
579
580
                    if reached_stop_revision_id or rev_id == stop_revision_id:
580
581
                        # only do the merged revs of rev_id from now on
581
582
                        rev = self.repository.get_revision(rev_id)
637
638
        """Tell this branch object not to release the physical lock when this
638
639
        object is unlocked.
639
640
 
640
 
        If lock_write doesn't return a token, then this method is not supported.
 
641
        If lock_write doesn't return a token, then this method is not
 
642
        supported.
641
643
        """
642
644
        self.control_files.leave_in_place()
643
645
 
645
647
        """Tell this branch object to release the physical lock when this
646
648
        object is unlocked, even if it didn't originally acquire it.
647
649
 
648
 
        If lock_write doesn't return a token, then this method is not supported.
 
650
        If lock_write doesn't return a token, then this method is not
 
651
        supported.
649
652
        """
650
653
        self.control_files.dont_leave_in_place()
651
654
 
688
691
        """
689
692
        with self.lock_write():
690
693
            return InterBranch.get(from_branch, self).fetch(
691
 
                    last_revision, limit=limit)
 
694
                last_revision, limit=limit)
692
695
 
693
696
    def get_bound_location(self):
694
697
        """Return the URL of the branch we are bound to.
716
719
        :param revprops: Optional dictionary of revision properties.
717
720
        :param revision_id: Optional revision id.
718
721
        :param lossy: Whether to discard data that can not be natively
719
 
            represented, when pushing to a foreign VCS 
 
722
            represented, when pushing to a foreign VCS
720
723
        """
721
724
 
722
725
        if config_stack is None:
723
726
            config_stack = self.get_config_stack()
724
727
 
725
 
        return self.repository.get_commit_builder(self, parents, config_stack,
726
 
            timestamp, timezone, committer, revprops, revision_id,
727
 
            lossy)
 
728
        return self.repository.get_commit_builder(
 
729
            self, parents, config_stack, timestamp, timezone, committer,
 
730
            revprops, revision_id, lossy)
728
731
 
729
732
    def get_master_branch(self, possible_transports=None):
730
733
        """Return the branch we are bound to.
769
772
                if not graph.is_ancestor(last_rev, revision_id):
770
773
                    # our previous tip is not merged into stop_revision
771
774
                    raise errors.DivergedBranches(self, other_branch)
772
 
            revno = graph.find_distance_to_null(revision_id, known_revision_ids)
 
775
            revno = graph.find_distance_to_null(
 
776
                revision_id, known_revision_ids)
773
777
            self.set_last_revision_info(revno, revision_id)
774
778
 
775
779
    def set_parent(self, url):
783
787
                try:
784
788
                    url.encode('ascii')
785
789
                except UnicodeEncodeError:
786
 
                    raise urlutils.InvalidURL(url,
787
 
                        "Urls must be 7-bit ascii, "
 
790
                    raise urlutils.InvalidURL(
 
791
                        url, "Urls must be 7-bit ascii, "
788
792
                        "use breezy.urlutils.escape")
789
793
            url = urlutils.relative_url(self.base, url)
790
794
        with self.lock_write():
801
805
        if not self._format.supports_stacking():
802
806
            raise UnstackableBranchFormat(self._format, self.user_url)
803
807
        with self.lock_write():
804
 
            # XXX: Changing from one fallback repository to another does not check
805
 
            # that all the data you need is present in the new fallback.
 
808
            # XXX: Changing from one fallback repository to another does not
 
809
            # check that all the data you need is present in the new fallback.
806
810
            # Possibly it should.
807
811
            self._check_stackable_repo()
808
812
            if not url:
809
813
                try:
810
 
                    old_url = self.get_stacked_on_url()
 
814
                    self.get_stacked_on_url()
811
815
                except (errors.NotStacked, UnstackableBranchFormat,
812
 
                    errors.UnstackableRepositoryFormat):
 
816
                        errors.UnstackableRepositoryFormat):
813
817
                    return
814
818
                self._unstack()
815
819
            else:
816
 
                self._activate_fallback_location(url,
817
 
                    possible_transports=[self.controldir.root_transport])
 
820
                self._activate_fallback_location(
 
821
                    url, possible_transports=[self.controldir.root_transport])
818
822
            # write this out after the repository is stacked to avoid setting a
819
823
            # stacked config that doesn't work.
820
824
            self._set_config_location('stacked_on_location', url)
828
832
            pb.update(gettext("Unstacking"))
829
833
            # The basic approach here is to fetch the tip of the branch,
830
834
            # including all available ghosts, from the existing stacked
831
 
            # repository into a new repository object without the fallbacks. 
 
835
            # repository into a new repository object without the fallbacks.
832
836
            #
833
837
            # XXX: See <https://launchpad.net/bugs/397286> - this may not be
834
838
            # correct for CHKMap repostiories
835
839
            old_repository = self.repository
836
840
            if len(old_repository._fallback_repositories) != 1:
837
 
                raise AssertionError("can't cope with fallback repositories "
838
 
                    "of %r (fallbacks: %r)" % (old_repository,
839
 
                        old_repository._fallback_repositories))
 
841
                raise AssertionError(
 
842
                    "can't cope with fallback repositories "
 
843
                    "of %r (fallbacks: %r)" % (
 
844
                        old_repository, old_repository._fallback_repositories))
840
845
            # Open the new repository object.
841
846
            # Repositories don't offer an interface to remove fallback
842
847
            # repositories today; take the conceptually simpler option and just
849
854
                self.controldir.root_transport.base)
850
855
            new_repository = new_bzrdir.find_repository()
851
856
            if new_repository._fallback_repositories:
852
 
                raise AssertionError("didn't expect %r to have "
853
 
                    "fallback_repositories"
 
857
                raise AssertionError(
 
858
                    "didn't expect %r to have fallback_repositories"
854
859
                    % (self.repository,))
855
860
            # Replace self.repository with the new repository.
856
861
            # Do our best to transfer the lock state (i.e. lock-tokens and
883
888
            if old_lock_count == 0:
884
889
                raise AssertionError(
885
890
                    'old_repository should have been locked at least once.')
886
 
            for i in range(old_lock_count-1):
 
891
            for i in range(old_lock_count - 1):
887
892
                self.repository.lock_write()
888
893
            # Fetch from the old repository into the new.
889
894
            with old_repository.lock_read():
894
899
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
895
900
                except errors.TagsNotSupported:
896
901
                    tags_to_fetch = set()
897
 
                fetch_spec = vf_search.NotInOtherForRevs(self.repository,
898
 
                    old_repository, required_ids=[self.last_revision()],
 
902
                fetch_spec = vf_search.NotInOtherForRevs(
 
903
                    self.repository, old_repository,
 
904
                    required_ids=[self.last_revision()],
899
905
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
900
906
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
901
907
 
983
989
        """
984
990
        with self.lock_read():
985
991
            if self._last_revision_info_cache is None:
986
 
                self._last_revision_info_cache = self._read_last_revision_info()
 
992
                self._last_revision_info_cache = (
 
993
                    self._read_last_revision_info())
987
994
            return self._last_revision_info_cache
988
995
 
989
996
    def _read_last_revision_info(self):
1043
1050
 
1044
1051
        :returns: PullResult instance
1045
1052
        """
1046
 
        return InterBranch.get(source, self).pull(overwrite=overwrite,
1047
 
            stop_revision=stop_revision,
 
1053
        return InterBranch.get(source, self).pull(
 
1054
            overwrite=overwrite, stop_revision=stop_revision,
1048
1055
            possible_transports=possible_transports, *args, **kwargs)
1049
1056
 
1050
1057
    def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1051
 
            *args, **kwargs):
 
1058
             *args, **kwargs):
1052
1059
        """Mirror this branch into target.
1053
1060
 
1054
1061
        This branch is considered to be 'local', having low latency.
1055
1062
        """
1056
 
        return InterBranch.get(self, target).push(overwrite, stop_revision,
1057
 
            lossy, *args, **kwargs)
 
1063
        return InterBranch.get(self, target).push(
 
1064
            overwrite, stop_revision, lossy, *args, **kwargs)
1058
1065
 
1059
1066
    def basis_tree(self):
1060
1067
        """Return `Tree` object for last revision."""
1076
1083
            parent = urlutils.local_path_to_url(parent)
1077
1084
        try:
1078
1085
            return urlutils.join(self.base[:-1], parent)
1079
 
        except urlutils.InvalidURLJoin as e:
 
1086
        except urlutils.InvalidURLJoin:
1080
1087
            raise errors.InaccessibleParent(parent, self.user_url)
1081
1088
 
1082
1089
    def _get_parent_location(self):
1208
1215
        return result
1209
1216
 
1210
1217
    def sprout(self, to_controldir, revision_id=None, repository_policy=None,
1211
 
            repository=None, lossy=False):
 
1218
               repository=None, lossy=False):
1212
1219
        """Create a new line of development from the branch, into to_controldir.
1213
1220
 
1214
1221
        to_controldir controls the branch format.
1216
1223
        revision_id: if not None, the revision history in the new branch will
1217
1224
                     be truncated to end with revision_id.
1218
1225
        """
1219
 
        if (repository_policy is not None and
1220
 
            repository_policy.requires_stacking()):
 
1226
        if (repository_policy is not None
 
1227
                and repository_policy.requires_stacking()):
1221
1228
            to_controldir._format.require_stacking(_skip_repo=True)
1222
1229
        result = to_controldir.create_branch(repository=repository)
1223
1230
        if lossy:
1251
1258
        else:
1252
1259
            graph = self.repository.get_graph()
1253
1260
            try:
1254
 
                revno = graph.find_distance_to_null(revision_id, 
1255
 
                    [(source_revision_id, source_revno)])
 
1261
                revno = graph.find_distance_to_null(
 
1262
                    revision_id, [(source_revision_id, source_revno)])
1256
1263
            except errors.GhostRevisionsHaveNoRevno:
1257
1264
                # Default to 1, if we can't find anything else
1258
1265
                revno = 1
1303
1310
            if actual_revno != last_revno:
1304
1311
                result.errors.append(errors.BzrCheckError(
1305
1312
                    'revno does not match len(mainline) %s != %s' % (
1306
 
                    last_revno, actual_revno)))
 
1313
                        last_revno, actual_revno)))
1307
1314
            # TODO: We should probably also check that self.revision_history
1308
1315
            # matches the repository for older branch formats.
1309
 
            # If looking for the code that cross-checks repository parents against
1310
 
            # the Graph.iter_lefthand_ancestry output, that is now a repository
1311
 
            # specific check.
 
1316
            # If looking for the code that cross-checks repository parents
 
1317
            # against the Graph.iter_lefthand_ancestry output, that is now a
 
1318
            # repository specific check.
1312
1319
            return result
1313
1320
 
1314
1321
    def _get_checkout_format(self, lightweight=False):
1320
1327
        return format
1321
1328
 
1322
1329
    def create_clone_on_transport(self, to_transport, revision_id=None,
1323
 
        stacked_on=None, create_prefix=False, use_existing_dir=False,
1324
 
        no_tree=None):
 
1330
                                  stacked_on=None, create_prefix=False,
 
1331
                                  use_existing_dir=False, no_tree=None):
1325
1332
        """Create a clone of this branch and its bzrdir.
1326
1333
 
1327
1334
        :param to_transport: The transport to clone onto.
1334
1341
        """
1335
1342
        # XXX: Fix the bzrdir API to allow getting the branch back from the
1336
1343
        # clone call. Or something. 20090224 RBC/spiv.
1337
 
        # XXX: Should this perhaps clone colocated branches as well, 
 
1344
        # XXX: Should this perhaps clone colocated branches as well,
1338
1345
        # rather than just the default branch? 20100319 JRV
1339
1346
        if revision_id is None:
1340
1347
            revision_id = self.last_revision()
1341
 
        dir_to = self.controldir.clone_on_transport(to_transport,
1342
 
            revision_id=revision_id, stacked_on=stacked_on,
 
1348
        dir_to = self.controldir.clone_on_transport(
 
1349
            to_transport, revision_id=revision_id, stacked_on=stacked_on,
1343
1350
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
1344
1351
            no_tree=no_tree)
1345
1352
        return dir_to.open_branch()
1376
1383
                pass
1377
1384
            else:
1378
1385
                raise errors.AlreadyControlDirError(t.base)
1379
 
            if checkout.control_transport.base == self.controldir.control_transport.base:
 
1386
            if (checkout.control_transport.base
 
1387
                    == self.controldir.control_transport.base):
1380
1388
                # When checking out to the same control directory,
1381
1389
                # always create a lightweight checkout
1382
1390
                lightweight = True
1385
1393
            from_branch = checkout.set_branch_reference(target_branch=self)
1386
1394
        else:
1387
1395
            policy = checkout.determine_repository_policy()
1388
 
            repo = policy.acquire_repository()[0]
 
1396
            policy.acquire_repository()
1389
1397
            checkout_branch = checkout.create_branch()
1390
1398
            checkout_branch.bind(self)
1391
1399
            # pull up to the specified revision_id to set the initial
1400
1408
        with basis_tree.lock_read():
1401
1409
            for path, file_id in basis_tree.iter_references():
1402
1410
                reference_parent = self.reference_parent(path, file_id)
1403
 
                reference_parent.create_checkout(tree.abspath(path),
1404
 
                    basis_tree.get_reference_revision(path, file_id),
1405
 
                    lightweight)
 
1411
                reference_parent.create_checkout(
 
1412
                    tree.abspath(path),
 
1413
                    basis_tree.get_reference_revision(path), lightweight)
1406
1414
        return tree
1407
1415
 
1408
1416
    def reconcile(self, thorough=True):
1609
1617
        raise NotImplementedError(self.network_name)
1610
1618
 
1611
1619
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
1612
 
            found_repository=None, possible_transports=None):
 
1620
             found_repository=None, possible_transports=None):
1613
1621
        """Return the branch object for controldir.
1614
1622
 
1615
1623
        :param controldir: A ControlDir that contains a branch.
1631
1639
 
1632
1640
    def supports_leaving_lock(self):
1633
1641
        """True if this format supports leaving locks in place."""
1634
 
        return False # by default
 
1642
        return False  # by default
1635
1643
 
1636
1644
    def __str__(self):
1637
1645
        return self.get_format_description().rstrip()
1667
1675
        notified.
1668
1676
        """
1669
1677
        Hooks.__init__(self, "breezy.branch", "Branch.hooks")
1670
 
        self.add_hook('open',
 
1678
        self.add_hook(
 
1679
            'open',
1671
1680
            "Called with the Branch object that has been opened after a "
1672
1681
            "branch is opened.", (1, 8))
1673
 
        self.add_hook('post_push',
 
1682
        self.add_hook(
 
1683
            'post_push',
1674
1684
            "Called after a push operation completes. post_push is called "
1675
 
            "with a breezy.branch.BranchPushResult object and only runs in the "
1676
 
            "bzr client.", (0, 15))
1677
 
        self.add_hook('post_pull',
 
1685
            "with a breezy.branch.BranchPushResult object and only runs in "
 
1686
            "the bzr client.", (0, 15))
 
1687
        self.add_hook(
 
1688
            'post_pull',
1678
1689
            "Called after a pull operation completes. post_pull is called "
1679
1690
            "with a breezy.branch.PullResult object and only runs in the "
1680
1691
            "bzr client.", (0, 15))
1681
 
        self.add_hook('pre_commit',
 
1692
        self.add_hook(
 
1693
            'pre_commit',
1682
1694
            "Called after a commit is calculated but before it is "
1683
1695
            "completed. pre_commit is called with (local, master, old_revno, "
1684
1696
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1688
1700
            " future_tree is an in-memory tree obtained from "
1689
1701
            "CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1690
1702
            "tree.", (0, 91))
1691
 
        self.add_hook('post_commit',
 
1703
        self.add_hook(
 
1704
            'post_commit',
1692
1705
            "Called in the bzr client after a commit has completed. "
1693
1706
            "post_commit is called with (local, master, old_revno, old_revid, "
1694
1707
            "new_revno, new_revid). old_revid is NULL_REVISION for the first "
1695
1708
            "commit to a branch.", (0, 15))
1696
 
        self.add_hook('post_uncommit',
 
1709
        self.add_hook(
 
1710
            'post_uncommit',
1697
1711
            "Called in the bzr client after an uncommit completes. "
1698
1712
            "post_uncommit is called with (local, master, old_revno, "
1699
1713
            "old_revid, new_revno, new_revid) where local is the local branch "
1700
1714
            "or None, master is the target branch, and an empty branch "
1701
1715
            "receives new_revno of 0, new_revid of None.", (0, 15))
1702
 
        self.add_hook('pre_change_branch_tip',
 
1716
        self.add_hook(
 
1717
            'pre_change_branch_tip',
1703
1718
            "Called in bzr client and server before a change to the tip of a "
1704
1719
            "branch is made. pre_change_branch_tip is called with a "
1705
1720
            "breezy.branch.ChangeBranchTipParams. Note that push, pull, "
1706
1721
            "commit, uncommit will all trigger this hook.", (1, 6))
1707
 
        self.add_hook('post_change_branch_tip',
 
1722
        self.add_hook(
 
1723
            'post_change_branch_tip',
1708
1724
            "Called in bzr client and server after a change to the tip of a "
1709
1725
            "branch is made. post_change_branch_tip is called with a "
1710
1726
            "breezy.branch.ChangeBranchTipParams. Note that push, pull, "
1711
1727
            "commit, uncommit will all trigger this hook.", (1, 4))
1712
 
        self.add_hook('transform_fallback_location',
 
1728
        self.add_hook(
 
1729
            'transform_fallback_location',
1713
1730
            "Called when a stacked branch is activating its fallback "
1714
1731
            "locations. transform_fallback_location is called with (branch, "
1715
1732
            "url), and should return a new url. Returning the same url "
1721
1738
            "multiple hooks installed for transform_fallback_location, "
1722
1739
            "all are called with the url returned from the previous hook."
1723
1740
            "The order is however undefined.", (1, 9))
1724
 
        self.add_hook('automatic_tag_name',
 
1741
        self.add_hook(
 
1742
            'automatic_tag_name',
1725
1743
            "Called to determine an automatic tag name for a revision. "
1726
1744
            "automatic_tag_name is called with (branch, revision_id) and "
1727
1745
            "should return a tag name or None if no tag name could be "
1728
1746
            "determined. The first non-None tag name returned will be used.",
1729
1747
            (2, 2))
1730
 
        self.add_hook('post_branch_init',
 
1748
        self.add_hook(
 
1749
            'post_branch_init',
1731
1750
            "Called after new branch initialization completes. "
1732
1751
            "post_branch_init is called with a "
1733
1752
            "breezy.branch.BranchInitHookParams. "
1734
1753
            "Note that init, branch and checkout (both heavyweight and "
1735
1754
            "lightweight) will all trigger this hook.", (2, 2))
1736
 
        self.add_hook('post_switch',
 
1755
        self.add_hook(
 
1756
            'post_switch',
1737
1757
            "Called after a checkout switches branch. "
1738
1758
            "post_switch is called with a "
1739
1759
            "breezy.branch.SwitchHookParams.", (2, 2))
1740
1760
 
1741
1761
 
1742
 
 
1743
1762
# install the default hooks into the Branch class.
1744
1763
Branch.hooks = BranchHooks()
1745
1764
 
1850
1869
        return self.__dict__ == other.__dict__
1851
1870
 
1852
1871
    def __repr__(self):
1853
 
        return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
1854
 
            self.control_dir, self.to_branch,
 
1872
        return "<%s for %s to (%s, %s)>" % (
 
1873
            self.__class__.__name__, self.control_dir, self.to_branch,
1855
1874
            self.revision_id)
1856
1875
 
1857
1876
 
1865
1884
 
1866
1885
    def get_default(self):
1867
1886
        """Return the current default format."""
1868
 
        if (self._default_format_key is not None and
1869
 
            self._default_format is None):
 
1887
        if (self._default_format_key is not None
 
1888
                and self._default_format is None):
1870
1889
            self._default_format = self.get(self._default_format_key)
1871
1890
        return self._default_format
1872
1891
 
2001
2020
            if self.old_revid != self.new_revid:
2002
2021
                note(gettext('Pushed up to revision %d.') % self.new_revno)
2003
2022
            if tag_updates:
2004
 
                note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
 
2023
                note(ngettext('%d tag updated.', '%d tags updated.',
 
2024
                              len(tag_updates)) % len(tag_updates))
2005
2025
            if self.old_revid == self.new_revid and not tag_updates:
2006
2026
                if not tag_conflicts:
2007
2027
                    note(gettext('No new revisions or tags to push.'))
2027
2047
            if any.
2028
2048
        """
2029
2049
        note(gettext('checked branch {0} format {1}').format(
2030
 
                                self.branch.user_url, self.branch._format))
 
2050
            self.branch.user_url, self.branch._format))
2031
2051
        for error in self.errors:
2032
2052
            note(gettext('found error:%s'), error)
2033
2053
 
2046
2066
    @classmethod
2047
2067
    def _get_branch_formats_to_test(klass):
2048
2068
        """Return an iterable of format tuples for testing.
2049
 
        
 
2069
 
2050
2070
        :return: An iterable of (from_format, to_format) to use when testing
2051
2071
            this InterBranch class. Each InterBranch class should define this
2052
2072
            method itself.
2144
2164
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
2145
2165
            fetch_spec_factory.source_repo = self.source.repository
2146
2166
            fetch_spec_factory.target_repo = self.target.repository
2147
 
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
 
2167
            fetch_spec_factory.target_repo_kind = (
 
2168
                fetch.TargetRepoKinds.PREEXISTING)
2148
2169
            fetch_spec_factory.limit = limit
2149
2170
            fetch_spec = fetch_spec_factory.make_fetch_spec()
2150
2171
            return self.target.repository.fetch(
2151
 
                    self.source.repository,
2152
 
                    fetch_spec=fetch_spec)
 
2172
                self.source.repository,
 
2173
                fetch_spec=fetch_spec)
2153
2174
 
2154
2175
    def _update_revisions(self, stop_revision=None, overwrite=False,
2155
 
            graph=None):
 
2176
                          graph=None):
2156
2177
        with self.source.lock_read(), self.target.lock_write():
2157
2178
            other_revno, other_last_revision = self.source.last_revision_info()
2158
 
            stop_revno = None # unknown
 
2179
            stop_revno = None  # unknown
2159
2180
            if stop_revision is None:
2160
2181
                stop_revision = other_last_revision
2161
2182
                if _mod_revision.is_null(stop_revision):
2184
2205
                if graph is None:
2185
2206
                    graph = self.target.repository.get_graph()
2186
2207
                this_revno, this_last_revision = \
2187
 
                        self.target.last_revision_info()
2188
 
                stop_revno = graph.find_distance_to_null(stop_revision,
2189
 
                                [(other_last_revision, other_revno),
2190
 
                                 (this_last_revision, this_revno)])
 
2208
                    self.target.last_revision_info()
 
2209
                stop_revno = graph.find_distance_to_null(
 
2210
                    stop_revision, [(other_last_revision, other_revno),
 
2211
                                    (this_last_revision, this_revno)])
2191
2212
            self.target.set_last_revision_info(stop_revno, stop_revision)
2192
2213
 
2193
2214
    def pull(self, overwrite=False, stop_revision=None,
2216
2237
                    source_is_master = False
2217
2238
            if not local and bound_location and not source_is_master:
2218
2239
                # not pulling from master, so we need to update master.
2219
 
                master_branch = self.target.get_master_branch(possible_transports)
 
2240
                master_branch = self.target.get_master_branch(
 
2241
                    possible_transports)
2220
2242
                master_branch.lock_write()
2221
2243
            try:
2222
2244
                if master_branch:
2223
2245
                    # pull from source into master.
2224
 
                    master_branch.pull(self.source, overwrite, stop_revision,
2225
 
                        run_hooks=False)
2226
 
                return self._pull(overwrite,
2227
 
                    stop_revision, _hook_master=master_branch,
 
2246
                    master_branch.pull(
 
2247
                        self.source, overwrite, stop_revision, run_hooks=False)
 
2248
                return self._pull(
 
2249
                    overwrite, stop_revision, _hook_master=master_branch,
2228
2250
                    run_hooks=run_hooks,
2229
2251
                    _override_hook_target=_override_hook_target,
2230
2252
                    merge_tags_to_master=not source_is_master)
2247
2269
            raise errors.LossyPushToSameVCS(self.source, self.target)
2248
2270
        # TODO: Public option to disable running hooks - should be trivial but
2249
2271
        # needs tests.
 
2272
 
2250
2273
        def _run_hooks():
2251
2274
            if _override_hook_source_branch:
2252
2275
                result.source_branch = _override_hook_source_branch
2258
2281
            if bound_location and self.target.base != bound_location:
2259
2282
                # there is a master branch.
2260
2283
                #
2261
 
                # XXX: Why the second check?  Is it even supported for a branch to
2262
 
                # be bound to itself? -- mbp 20070507
 
2284
                # XXX: Why the second check?  Is it even supported for a branch
 
2285
                # to be bound to itself? -- mbp 20070507
2263
2286
                master_branch = self.target.get_master_branch()
2264
2287
                with master_branch.lock_write():
2265
2288
                    # push into the master from the source branch.
2266
2289
                    master_inter = InterBranch.get(self.source, master_branch)
2267
2290
                    master_inter._basic_push(overwrite, stop_revision)
2268
 
                    # and push into the target branch from the source. Note that
2269
 
                    # we push from the source branch again, because it's considered
2270
 
                    # the highest bandwidth repository.
 
2291
                    # and push into the target branch from the source. Note
 
2292
                    # that we push from the source branch again, because it's
 
2293
                    # considered the highest bandwidth repository.
2271
2294
                    result = self._basic_push(overwrite, stop_revision)
2272
2295
                    result.master_branch = master_branch
2273
2296
                    result.local_branch = self.target
2299
2322
            # We assume that during 'push' this repository is closer than
2300
2323
            # the target.
2301
2324
            graph = self.source.repository.get_graph(self.target.repository)
2302
 
            self._update_revisions(stop_revision,
2303
 
                overwrite=("history" in overwrite),
2304
 
                graph=graph)
 
2325
            self._update_revisions(
 
2326
                stop_revision, overwrite=("history" in overwrite), graph=graph)
2305
2327
        if self.source._push_should_merge_tags():
2306
2328
            result.tag_updates, result.tag_conflicts = (
2307
2329
                self.source.tags.merge_to(
2308
 
                self.target.tags, "tags" in overwrite))
 
2330
                    self.target.tags, "tags" in overwrite))
2309
2331
        result.new_revno, result.new_revid = self.target.last_revision_info()
2310
2332
        return result
2311
2333
 
2312
2334
    def _pull(self, overwrite=False, stop_revision=None,
2313
 
             possible_transports=None, _hook_master=None, run_hooks=True,
2314
 
             _override_hook_target=None, local=False,
2315
 
             merge_tags_to_master=True):
 
2335
              possible_transports=None, _hook_master=None, run_hooks=True,
 
2336
              _override_hook_target=None, local=False,
 
2337
              merge_tags_to_master=True):
2316
2338
        """See Branch.pull.
2317
2339
 
2318
2340
        This function is the core worker, used by GenericInterBranch.pull to
2343
2365
            # the source one.
2344
2366
            self.source.update_references(self.target)
2345
2367
            graph = self.target.repository.get_graph(self.source.repository)
2346
 
            # TODO: Branch formats should have a flag that indicates 
 
2368
            # TODO: Branch formats should have a flag that indicates
2347
2369
            # that revno's are expensive, and pull() should honor that flag.
2348
2370
            # -- JRV20090506
2349
2371
            result.old_revno, result.old_revid = \
2350
2372
                self.target.last_revision_info()
2351
2373
            overwrite = _fix_overwrite_type(overwrite)
2352
 
            self._update_revisions(stop_revision,
2353
 
                overwrite=("history" in overwrite),
2354
 
                graph=graph)
2355
 
            # TODO: The old revid should be specified when merging tags, 
2356
 
            # so a tags implementation that versions tags can only 
 
2374
            self._update_revisions(
 
2375
                stop_revision, overwrite=("history" in overwrite), graph=graph)
 
2376
            # TODO: The old revid should be specified when merging tags,
 
2377
            # so a tags implementation that versions tags can only
2357
2378
            # pull in the most recent changes. -- JRV20090506
2358
2379
            result.tag_updates, result.tag_conflicts = (
2359
 
                self.source.tags.merge_to(self.target.tags,
2360
 
                    "tags" in overwrite,
 
2380
                self.source.tags.merge_to(
 
2381
                    self.target.tags, "tags" in overwrite,
2361
2382
                    ignore_master=not merge_tags_to_master))
2362
 
            result.new_revno, result.new_revid = self.target.last_revision_info()
 
2383
            result.new_revno, result.new_revid = (
 
2384
                self.target.last_revision_info())
2363
2385
            if _hook_master:
2364
2386
                result.master_branch = _hook_master
2365
2387
                result.local_branch = result.target_branch