~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: John Arbash Meinel
  • Date: 2010-08-30 21:23:49 UTC
  • mto: This revision was merged to the branch mainline in revision 5398.
  • Revision ID: john@arbash-meinel.com-20100830212349-figt9yz2cic6hy68
Remove the 'false' invocation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007, 2008, 2009 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
25
25
        bzrdir,
26
26
        cache_utf8,
27
27
        config as _mod_config,
 
28
        controldir,
28
29
        debug,
29
30
        errors,
30
31
        lockdir,
31
32
        lockable_files,
 
33
        remote,
32
34
        repository,
33
35
        revision as _mod_revision,
34
36
        rio,
35
37
        symbol_versioning,
36
38
        transport,
37
39
        tsort,
 
40
        ui,
38
41
        urlutils,
39
42
        )
40
43
from bzrlib.config import BranchConfig, TransportConfig
45
48
    )
46
49
""")
47
50
 
48
 
from bzrlib.decorators import needs_read_lock, needs_write_lock
 
51
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
49
52
from bzrlib.hooks import HookPoint, Hooks
50
53
from bzrlib.inter import InterObject
 
54
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
51
55
from bzrlib import registry
52
56
from bzrlib.symbol_versioning import (
53
57
    deprecated_in,
61
65
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
62
66
 
63
67
 
64
 
# TODO: Maybe include checks for common corruption of newlines, etc?
65
 
 
66
 
# TODO: Some operations like log might retrieve the same revisions
67
 
# repeatedly to calculate deltas.  We could perhaps have a weakref
68
 
# cache in memory to make this faster.  In general anything can be
69
 
# cached in memory between lock and unlock operations. .. nb thats
70
 
# what the transaction identity map provides
71
 
 
72
 
 
73
 
######################################################################
74
 
# branch objects
75
 
 
76
 
class Branch(object):
 
68
class Branch(controldir.ControlComponent):
77
69
    """Branch holding a history of revisions.
78
70
 
79
 
    base
80
 
        Base directory/url of the branch.
 
71
    :ivar base:
 
72
        Base directory/url of the branch; using control_url and
 
73
        control_transport is more standardized.
81
74
 
82
75
    hooks: An instance of BranchHooks.
83
76
    """
85
78
    # - RBC 20060112
86
79
    base = None
87
80
 
 
81
    @property
 
82
    def control_transport(self):
 
83
        return self._transport
 
84
 
 
85
    @property
 
86
    def user_transport(self):
 
87
        return self.bzrdir.user_transport
 
88
 
88
89
    def __init__(self, *ignored, **ignored_too):
89
90
        self.tags = self._format.make_tags(self)
90
91
        self._revision_history_cache = None
105
106
        """Activate the branch/repository from url as a fallback repository."""
106
107
        repo = self._get_fallback_repository(url)
107
108
        if repo.has_same_location(self.repository):
108
 
            raise errors.UnstackableLocationError(self.base, url)
 
109
            raise errors.UnstackableLocationError(self.user_url, url)
109
110
        self.repository.add_fallback_repository(repo)
110
111
 
111
112
    def break_lock(self):
148
149
        if self._partial_revision_history_cache[-1] == _mod_revision.NULL_REVISION:
149
150
            self._partial_revision_history_cache.pop()
150
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
 
151
160
    @staticmethod
152
161
    def open(base, _unsupported=False, possible_transports=None):
153
162
        """Open the branch rooted at base.
157
166
        """
158
167
        control = bzrdir.BzrDir.open(base, _unsupported,
159
168
                                     possible_transports=possible_transports)
160
 
        return control.open_branch(_unsupported)
 
169
        return control.open_branch(unsupported=_unsupported)
161
170
 
162
171
    @staticmethod
163
 
    def open_from_transport(transport, _unsupported=False):
 
172
    def open_from_transport(transport, name=None, _unsupported=False):
164
173
        """Open the branch rooted at transport"""
165
174
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
166
 
        return control.open_branch(_unsupported)
 
175
        return control.open_branch(name=name, unsupported=_unsupported)
167
176
 
168
177
    @staticmethod
169
178
    def open_containing(url, possible_transports=None):
190
199
        return self.supports_tags() and self.tags.get_tag_dict()
191
200
 
192
201
    def get_config(self):
 
202
        """Get a bzrlib.config.BranchConfig for this Branch.
 
203
 
 
204
        This can then be used to get and set configuration options for the
 
205
        branch.
 
206
 
 
207
        :return: A bzrlib.config.BranchConfig.
 
208
        """
193
209
        return BranchConfig(self)
194
210
 
195
211
    def _get_config(self):
207
223
    def _get_fallback_repository(self, url):
208
224
        """Get the repository we fallback to at url."""
209
225
        url = urlutils.join(self.base, url)
210
 
        a_bzrdir = bzrdir.BzrDir.open(url,
 
226
        a_branch = Branch.open(url,
211
227
            possible_transports=[self.bzrdir.root_transport])
212
 
        return a_bzrdir.open_branch().repository
 
228
        return a_branch.repository
213
229
 
214
230
    def _get_tags_bytes(self):
215
231
        """Get the bytes of a serialised tags dict.
231
247
        if not local and not config.has_explicit_nickname():
232
248
            try:
233
249
                master = self.get_master_branch(possible_transports)
 
250
                if master and self.user_url == master.user_url:
 
251
                    raise errors.RecursiveBind(self.user_url)
234
252
                if master is not None:
235
253
                    # return the master branch value
236
254
                    return master.nick
 
255
            except errors.RecursiveBind, e:
 
256
                raise e
237
257
            except errors.BzrError, e:
238
258
                # Silently fall back to local implicit nick if the master is
239
259
                # unavailable
276
296
        new_history.reverse()
277
297
        return new_history
278
298
 
279
 
    def lock_write(self):
 
299
    def lock_write(self, token=None):
 
300
        """Lock the branch for write operations.
 
301
 
 
302
        :param token: A token to permit reacquiring a previously held and
 
303
            preserved lock.
 
304
        :return: A BranchWriteLockResult.
 
305
        """
280
306
        raise NotImplementedError(self.lock_write)
281
307
 
282
308
    def lock_read(self):
 
309
        """Lock the branch for read operations.
 
310
 
 
311
        :return: A bzrlib.lock.LogicalLockResult.
 
312
        """
283
313
        raise NotImplementedError(self.lock_read)
284
314
 
285
315
    def unlock(self):
410
440
            * 'include' - the stop revision is the last item in the result
411
441
            * 'with-merges' - include the stop revision and all of its
412
442
              merged revisions in the result
 
443
            * 'with-merges-without-common-ancestry' - filter out revisions 
 
444
              that are in both ancestries
413
445
        :param direction: either 'reverse' or 'forward':
414
446
            * reverse means return the start_revision_id first, i.e.
415
447
              start at the most recent revision and go backwards in history
437
469
        # start_revision_id.
438
470
        if self._merge_sorted_revisions_cache is None:
439
471
            last_revision = self.last_revision()
440
 
            graph = self.repository.get_graph()
441
 
            parent_map = dict(((key, value) for key, value in
442
 
                     graph.iter_ancestry([last_revision]) if value is not None))
443
 
            revision_graph = repository._strip_NULL_ghosts(parent_map)
444
 
            revs = tsort.merge_sort(revision_graph, last_revision, None,
445
 
                generate_revno=True)
446
 
            # Drop the sequence # before caching
447
 
            self._merge_sorted_revisions_cache = [r[1:] for r in revs]
448
 
 
 
472
            known_graph = self.repository.get_known_graph_ancestry(
 
473
                [last_revision])
 
474
            self._merge_sorted_revisions_cache = known_graph.merge_sort(
 
475
                last_revision)
449
476
        filtered = self._filter_merge_sorted_revisions(
450
477
            self._merge_sorted_revisions_cache, start_revision_id,
451
478
            stop_revision_id, stop_rule)
 
479
        # Make sure we don't return revisions that are not part of the
 
480
        # start_revision_id ancestry.
 
481
        filtered = self._filter_start_non_ancestors(filtered)
452
482
        if direction == 'reverse':
453
483
            return filtered
454
484
        if direction == 'forward':
461
491
        """Iterate over an inclusive range of sorted revisions."""
462
492
        rev_iter = iter(merge_sorted_revisions)
463
493
        if start_revision_id is not None:
464
 
            for rev_id, depth, revno, end_of_merge in rev_iter:
 
494
            for node in rev_iter:
 
495
                rev_id = node.key[-1]
465
496
                if rev_id != start_revision_id:
466
497
                    continue
467
498
                else:
468
499
                    # The decision to include the start or not
469
500
                    # depends on the stop_rule if a stop is provided
470
 
                    rev_iter = chain(
471
 
                        iter([(rev_id, depth, revno, end_of_merge)]),
472
 
                        rev_iter)
 
501
                    # so pop this node back into the iterator
 
502
                    rev_iter = chain(iter([node]), rev_iter)
473
503
                    break
474
504
        if stop_revision_id is None:
475
 
            for rev_id, depth, revno, end_of_merge in rev_iter:
476
 
                yield rev_id, depth, revno, end_of_merge
 
505
            # Yield everything
 
506
            for node in rev_iter:
 
507
                rev_id = node.key[-1]
 
508
                yield (rev_id, node.merge_depth, node.revno,
 
509
                       node.end_of_merge)
477
510
        elif stop_rule == 'exclude':
478
 
            for rev_id, depth, revno, end_of_merge in rev_iter:
 
511
            for node in rev_iter:
 
512
                rev_id = node.key[-1]
479
513
                if rev_id == stop_revision_id:
480
514
                    return
481
 
                yield rev_id, depth, revno, end_of_merge
 
515
                yield (rev_id, node.merge_depth, node.revno,
 
516
                       node.end_of_merge)
482
517
        elif stop_rule == 'include':
483
 
            for rev_id, depth, revno, end_of_merge in rev_iter:
484
 
                yield rev_id, depth, revno, end_of_merge
 
518
            for node in rev_iter:
 
519
                rev_id = node.key[-1]
 
520
                yield (rev_id, node.merge_depth, node.revno,
 
521
                       node.end_of_merge)
485
522
                if rev_id == stop_revision_id:
486
523
                    return
 
524
        elif stop_rule == 'with-merges-without-common-ancestry':
 
525
            # We want to exclude all revisions that are already part of the
 
526
            # stop_revision_id ancestry.
 
527
            graph = self.repository.get_graph()
 
528
            ancestors = graph.find_unique_ancestors(start_revision_id,
 
529
                                                    [stop_revision_id])
 
530
            for node in rev_iter:
 
531
                rev_id = node.key[-1]
 
532
                if rev_id not in ancestors:
 
533
                    continue
 
534
                yield (rev_id, node.merge_depth, node.revno,
 
535
                       node.end_of_merge)
487
536
        elif stop_rule == 'with-merges':
488
537
            stop_rev = self.repository.get_revision(stop_revision_id)
489
538
            if stop_rev.parent_ids:
490
539
                left_parent = stop_rev.parent_ids[0]
491
540
            else:
492
541
                left_parent = _mod_revision.NULL_REVISION
493
 
            for rev_id, depth, revno, end_of_merge in rev_iter:
 
542
            # left_parent is the actual revision we want to stop logging at,
 
543
            # since we want to show the merged revisions after the stop_rev too
 
544
            reached_stop_revision_id = False
 
545
            revision_id_whitelist = []
 
546
            for node in rev_iter:
 
547
                rev_id = node.key[-1]
494
548
                if rev_id == left_parent:
 
549
                    # reached the left parent after the stop_revision
495
550
                    return
496
 
                yield rev_id, depth, revno, end_of_merge
 
551
                if (not reached_stop_revision_id or
 
552
                        rev_id in revision_id_whitelist):
 
553
                    yield (rev_id, node.merge_depth, node.revno,
 
554
                       node.end_of_merge)
 
555
                    if reached_stop_revision_id or rev_id == stop_revision_id:
 
556
                        # only do the merged revs of rev_id from now on
 
557
                        rev = self.repository.get_revision(rev_id)
 
558
                        if rev.parent_ids:
 
559
                            reached_stop_revision_id = True
 
560
                            revision_id_whitelist.extend(rev.parent_ids)
497
561
        else:
498
562
            raise ValueError('invalid stop_rule %r' % stop_rule)
499
563
 
 
564
    def _filter_start_non_ancestors(self, rev_iter):
 
565
        # If we started from a dotted revno, we want to consider it as a tip
 
566
        # and don't want to yield revisions that are not part of its
 
567
        # ancestry. Given the order guaranteed by the merge sort, we will see
 
568
        # uninteresting descendants of the first parent of our tip before the
 
569
        # tip itself.
 
570
        first = rev_iter.next()
 
571
        (rev_id, merge_depth, revno, end_of_merge) = first
 
572
        yield first
 
573
        if not merge_depth:
 
574
            # We start at a mainline revision so by definition, all others
 
575
            # revisions in rev_iter are ancestors
 
576
            for node in rev_iter:
 
577
                yield node
 
578
 
 
579
        clean = False
 
580
        whitelist = set()
 
581
        pmap = self.repository.get_parent_map([rev_id])
 
582
        parents = pmap.get(rev_id, [])
 
583
        if parents:
 
584
            whitelist.update(parents)
 
585
        else:
 
586
            # If there is no parents, there is nothing of interest left
 
587
 
 
588
            # FIXME: It's hard to test this scenario here as this code is never
 
589
            # called in that case. -- vila 20100322
 
590
            return
 
591
 
 
592
        for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
 
593
            if not clean:
 
594
                if rev_id in whitelist:
 
595
                    pmap = self.repository.get_parent_map([rev_id])
 
596
                    parents = pmap.get(rev_id, [])
 
597
                    whitelist.remove(rev_id)
 
598
                    whitelist.update(parents)
 
599
                    if merge_depth == 0:
 
600
                        # We've reached the mainline, there is nothing left to
 
601
                        # filter
 
602
                        clean = True
 
603
                else:
 
604
                    # A revision that is not part of the ancestry of our
 
605
                    # starting revision.
 
606
                    continue
 
607
            yield (rev_id, merge_depth, revno, end_of_merge)
 
608
 
500
609
    def leave_lock_in_place(self):
501
610
        """Tell this branch object not to release the physical lock when this
502
611
        object is unlocked.
519
628
        :param other: The branch to bind to
520
629
        :type other: Branch
521
630
        """
522
 
        raise errors.UpgradeRequired(self.base)
 
631
        raise errors.UpgradeRequired(self.user_url)
523
632
 
524
633
    def set_append_revisions_only(self, enabled):
525
634
        if not self._format.supports_set_append_revisions_only():
526
 
            raise errors.UpgradeRequired(self.base)
 
635
            raise errors.UpgradeRequired(self.user_url)
527
636
        if enabled:
528
637
            value = 'True'
529
638
        else:
577
686
    def get_old_bound_location(self):
578
687
        """Return the URL of the branch we used to be bound to
579
688
        """
580
 
        raise errors.UpgradeRequired(self.base)
 
689
        raise errors.UpgradeRequired(self.user_url)
581
690
 
582
691
    def get_commit_builder(self, parents, config=None, timestamp=None,
583
692
                           timezone=None, committer=None, revprops=None,
661
770
            stacking.
662
771
        """
663
772
        if not self._format.supports_stacking():
664
 
            raise errors.UnstackableBranchFormat(self._format, self.base)
 
773
            raise errors.UnstackableBranchFormat(self._format, self.user_url)
 
774
        # XXX: Changing from one fallback repository to another does not check
 
775
        # that all the data you need is present in the new fallback.
 
776
        # Possibly it should.
665
777
        self._check_stackable_repo()
666
778
        if not url:
667
779
            try:
669
781
            except (errors.NotStacked, errors.UnstackableBranchFormat,
670
782
                errors.UnstackableRepositoryFormat):
671
783
                return
672
 
            url = ''
673
 
            # XXX: Lock correctness - should unlock our old repo if we were
674
 
            # locked.
675
 
            # repositories don't offer an interface to remove fallback
676
 
            # repositories today; take the conceptually simpler option and just
677
 
            # reopen it.
678
 
            self.repository = self.bzrdir.find_repository()
679
 
            self.repository.lock_write()
680
 
            # for every revision reference the branch has, ensure it is pulled
681
 
            # in.
682
 
            source_repository = self._get_fallback_repository(old_url)
683
 
            for revision_id in chain([self.last_revision()],
684
 
                self.tags.get_reverse_tag_dict()):
685
 
                self.repository.fetch(source_repository, revision_id,
686
 
                    find_ghosts=True)
 
784
            self._unstack()
687
785
        else:
688
786
            self._activate_fallback_location(url)
689
787
        # write this out after the repository is stacked to avoid setting a
690
788
        # stacked config that doesn't work.
691
789
        self._set_config_location('stacked_on_location', url)
692
790
 
 
791
    def _unstack(self):
 
792
        """Change a branch to be unstacked, copying data as needed.
 
793
        
 
794
        Don't call this directly, use set_stacked_on_url(None).
 
795
        """
 
796
        pb = ui.ui_factory.nested_progress_bar()
 
797
        try:
 
798
            pb.update("Unstacking")
 
799
            # The basic approach here is to fetch the tip of the branch,
 
800
            # including all available ghosts, from the existing stacked
 
801
            # repository into a new repository object without the fallbacks. 
 
802
            #
 
803
            # XXX: See <https://launchpad.net/bugs/397286> - this may not be
 
804
            # correct for CHKMap repostiories
 
805
            old_repository = self.repository
 
806
            if len(old_repository._fallback_repositories) != 1:
 
807
                raise AssertionError("can't cope with fallback repositories "
 
808
                    "of %r" % (self.repository,))
 
809
            # Open the new repository object.
 
810
            # Repositories don't offer an interface to remove fallback
 
811
            # repositories today; take the conceptually simpler option and just
 
812
            # reopen it.  We reopen it starting from the URL so that we
 
813
            # get a separate connection for RemoteRepositories and can
 
814
            # stream from one of them to the other.  This does mean doing
 
815
            # separate SSH connection setup, but unstacking is not a
 
816
            # common operation so it's tolerable.
 
817
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
 
818
            new_repository = new_bzrdir.find_repository()
 
819
            if new_repository._fallback_repositories:
 
820
                raise AssertionError("didn't expect %r to have "
 
821
                    "fallback_repositories"
 
822
                    % (self.repository,))
 
823
            # Replace self.repository with the new repository.
 
824
            # Do our best to transfer the lock state (i.e. lock-tokens and
 
825
            # lock count) of self.repository to the new repository.
 
826
            lock_token = old_repository.lock_write().repository_token
 
827
            self.repository = new_repository
 
828
            if isinstance(self, remote.RemoteBranch):
 
829
                # Remote branches can have a second reference to the old
 
830
                # repository that need to be replaced.
 
831
                if self._real_branch is not None:
 
832
                    self._real_branch.repository = new_repository
 
833
            self.repository.lock_write(token=lock_token)
 
834
            if lock_token is not None:
 
835
                old_repository.leave_lock_in_place()
 
836
            old_repository.unlock()
 
837
            if lock_token is not None:
 
838
                # XXX: self.repository.leave_lock_in_place() before this
 
839
                # function will not be preserved.  Fortunately that doesn't
 
840
                # affect the current default format (2a), and would be a
 
841
                # corner-case anyway.
 
842
                #  - Andrew Bennetts, 2010/06/30
 
843
                self.repository.dont_leave_lock_in_place()
 
844
            old_lock_count = 0
 
845
            while True:
 
846
                try:
 
847
                    old_repository.unlock()
 
848
                except errors.LockNotHeld:
 
849
                    break
 
850
                old_lock_count += 1
 
851
            if old_lock_count == 0:
 
852
                raise AssertionError(
 
853
                    'old_repository should have been locked at least once.')
 
854
            for i in range(old_lock_count-1):
 
855
                self.repository.lock_write()
 
856
            # Fetch from the old repository into the new.
 
857
            old_repository.lock_read()
 
858
            try:
 
859
                # XXX: If you unstack a branch while it has a working tree
 
860
                # with a pending merge, the pending-merged revisions will no
 
861
                # longer be present.  You can (probably) revert and remerge.
 
862
                #
 
863
                # XXX: This only fetches up to the tip of the repository; it
 
864
                # doesn't bring across any tags.  That's fairly consistent
 
865
                # with how branch works, but perhaps not ideal.
 
866
                self.repository.fetch(old_repository,
 
867
                    revision_id=self.last_revision(),
 
868
                    find_ghosts=True)
 
869
            finally:
 
870
                old_repository.unlock()
 
871
        finally:
 
872
            pb.finished()
693
873
 
694
874
    def _set_tags_bytes(self, bytes):
695
875
        """Mirror method for _get_tags_bytes.
775
955
 
776
956
    def unbind(self):
777
957
        """Older format branches cannot bind or unbind."""
778
 
        raise errors.UpgradeRequired(self.base)
 
958
        raise errors.UpgradeRequired(self.user_url)
779
959
 
780
960
    def last_revision(self):
781
961
        """Return last revision id, or NULL_REVISION."""
822
1002
                raise errors.NoSuchRevision(self, stop_revision)
823
1003
        return other_history[self_len:stop_revision]
824
1004
 
825
 
    @needs_write_lock
826
1005
    def update_revisions(self, other, stop_revision=None, overwrite=False,
827
1006
                         graph=None):
828
1007
        """Pull in new perfect-fit revisions.
877
1056
            self._extend_partial_history(distance_from_last)
878
1057
        return self._partial_revision_history_cache[distance_from_last]
879
1058
 
880
 
    @needs_write_lock
881
1059
    def pull(self, source, overwrite=False, stop_revision=None,
882
1060
             possible_transports=None, *args, **kwargs):
883
1061
        """Mirror source into this branch.
941
1119
        try:
942
1120
            return urlutils.join(self.base[:-1], parent)
943
1121
        except errors.InvalidURLJoin, e:
944
 
            raise errors.InaccessibleParent(parent, self.base)
 
1122
            raise errors.InaccessibleParent(parent, self.user_url)
945
1123
 
946
1124
    def _get_parent_location(self):
947
1125
        raise NotImplementedError(self._get_parent_location)
1032
1210
        params = ChangeBranchTipParams(
1033
1211
            self, old_revno, new_revno, old_revid, new_revid)
1034
1212
        for hook in hooks:
1035
 
            try:
1036
 
                hook(params)
1037
 
            except errors.TipChangeRejected:
1038
 
                raise
1039
 
            except Exception:
1040
 
                exc_info = sys.exc_info()
1041
 
                hook_name = Branch.hooks.get_hook_name(hook)
1042
 
                raise errors.HookFailed(
1043
 
                    'pre_change_branch_tip', hook_name, exc_info)
 
1213
            hook(params)
1044
1214
 
1045
1215
    @needs_write_lock
1046
1216
    def update(self):
1095
1265
        revision_id: if not None, the revision history in the new branch will
1096
1266
                     be truncated to end with revision_id.
1097
1267
        """
 
1268
        if (repository_policy is not None and
 
1269
            repository_policy.requires_stacking()):
 
1270
            to_bzrdir._format.require_stacking(_skip_repo=True)
1098
1271
        result = to_bzrdir.create_branch()
1099
1272
        result.lock_write()
1100
1273
        try:
1131
1304
                revno = 1
1132
1305
        destination.set_last_revision_info(revno, revision_id)
1133
1306
 
1134
 
    @needs_read_lock
1135
1307
    def copy_content_into(self, destination, revision_id=None):
1136
1308
        """Copy the content of self into destination.
1137
1309
 
1138
1310
        revision_id: if not None, the revision history in the new branch will
1139
1311
                     be truncated to end with revision_id.
1140
1312
        """
1141
 
        self.update_references(destination)
1142
 
        self._synchronize_history(destination, revision_id)
1143
 
        try:
1144
 
            parent = self.get_parent()
1145
 
        except errors.InaccessibleParent, e:
1146
 
            mutter('parent was not accessible to copy: %s', e)
1147
 
        else:
1148
 
            if parent:
1149
 
                destination.set_parent(parent)
1150
 
        if self._push_should_merge_tags():
1151
 
            self.tags.merge_to(destination.tags)
 
1313
        return InterBranch.get(self, destination).copy_content_into(
 
1314
            revision_id=revision_id)
1152
1315
 
1153
1316
    def update_references(self, target):
1154
1317
        if not getattr(self._format, 'supports_reference_locations', False):
1168
1331
        target._set_all_reference_info(target_reference_dict)
1169
1332
 
1170
1333
    @needs_read_lock
1171
 
    def check(self):
 
1334
    def check(self, refs):
1172
1335
        """Check consistency of the branch.
1173
1336
 
1174
1337
        In particular this checks that revisions given in the revision-history
1177
1340
 
1178
1341
        Callers will typically also want to check the repository.
1179
1342
 
 
1343
        :param refs: Calculated refs for this branch as specified by
 
1344
            branch._get_check_refs()
1180
1345
        :return: A BranchCheckResult.
1181
1346
        """
1182
 
        ret = BranchCheckResult(self)
1183
 
        mainline_parent_id = None
 
1347
        result = BranchCheckResult(self)
1184
1348
        last_revno, last_revision_id = self.last_revision_info()
1185
 
        real_rev_history = []
1186
 
        try:
1187
 
            for revid in self.repository.iter_reverse_revision_history(
1188
 
                last_revision_id):
1189
 
                real_rev_history.append(revid)
1190
 
        except errors.RevisionNotPresent:
1191
 
            ret.ghosts_in_mainline = True
1192
 
        else:
1193
 
            ret.ghosts_in_mainline = False
1194
 
        real_rev_history.reverse()
1195
 
        if len(real_rev_history) != last_revno:
1196
 
            raise errors.BzrCheckError('revno does not match len(mainline)'
1197
 
                ' %s != %s' % (last_revno, len(real_rev_history)))
1198
 
        # TODO: We should probably also check that real_rev_history actually
1199
 
        #       matches self.revision_history()
1200
 
        for revision_id in real_rev_history:
1201
 
            try:
1202
 
                revision = self.repository.get_revision(revision_id)
1203
 
            except errors.NoSuchRevision, e:
1204
 
                raise errors.BzrCheckError("mainline revision {%s} not in repository"
1205
 
                            % revision_id)
1206
 
            # In general the first entry on the revision history has no parents.
1207
 
            # But it's not illegal for it to have parents listed; this can happen
1208
 
            # in imports from Arch when the parents weren't reachable.
1209
 
            if mainline_parent_id is not None:
1210
 
                if mainline_parent_id not in revision.parent_ids:
1211
 
                    raise errors.BzrCheckError("previous revision {%s} not listed among "
1212
 
                                        "parents of {%s}"
1213
 
                                        % (mainline_parent_id, revision_id))
1214
 
            mainline_parent_id = revision_id
1215
 
        return ret
 
1349
        actual_revno = refs[('lefthand-distance', last_revision_id)]
 
1350
        if actual_revno != last_revno:
 
1351
            result.errors.append(errors.BzrCheckError(
 
1352
                'revno does not match len(mainline) %s != %s' % (
 
1353
                last_revno, actual_revno)))
 
1354
        # TODO: We should probably also check that self.revision_history
 
1355
        # matches the repository for older branch formats.
 
1356
        # If looking for the code that cross-checks repository parents against
 
1357
        # the iter_reverse_revision_history output, that is now a repository
 
1358
        # specific check.
 
1359
        return result
1216
1360
 
1217
1361
    def _get_checkout_format(self):
1218
1362
        """Return the most suitable metadir for a checkout of this branch.
1241
1385
        """
1242
1386
        # XXX: Fix the bzrdir API to allow getting the branch back from the
1243
1387
        # clone call. Or something. 20090224 RBC/spiv.
 
1388
        # XXX: Should this perhaps clone colocated branches as well, 
 
1389
        # rather than just the default branch? 20100319 JRV
1244
1390
        if revision_id is None:
1245
1391
            revision_id = self.last_revision()
1246
 
        try:
1247
 
            dir_to = self.bzrdir.clone_on_transport(to_transport,
1248
 
                revision_id=revision_id, stacked_on=stacked_on,
1249
 
                create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1250
 
        except errors.FileExists:
1251
 
            if not use_existing_dir:
1252
 
                raise
1253
 
        except errors.NoSuchFile:
1254
 
            if not create_prefix:
1255
 
                raise
 
1392
        dir_to = self.bzrdir.clone_on_transport(to_transport,
 
1393
            revision_id=revision_id, stacked_on=stacked_on,
 
1394
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1256
1395
        return dir_to.open_branch()
1257
1396
 
1258
1397
    def create_checkout(self, to_location, revision_id=None,
1277
1416
        if lightweight:
1278
1417
            format = self._get_checkout_format()
1279
1418
            checkout = format.initialize_on_transport(t)
1280
 
            from_branch = BranchReferenceFormat().initialize(checkout, self)
 
1419
            from_branch = BranchReferenceFormat().initialize(checkout, 
 
1420
                target_branch=self)
1281
1421
        else:
1282
1422
            format = self._get_checkout_format()
1283
1423
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1325
1465
    def supports_tags(self):
1326
1466
        return self._format.supports_tags()
1327
1467
 
 
1468
    def automatic_tag_name(self, revision_id):
 
1469
        """Try to automatically find the tag name for a revision.
 
1470
 
 
1471
        :param revision_id: Revision id of the revision.
 
1472
        :return: A tag name or None if no tag name could be determined.
 
1473
        """
 
1474
        for hook in Branch.hooks['automatic_tag_name']:
 
1475
            ret = hook(self, revision_id)
 
1476
            if ret is not None:
 
1477
                return ret
 
1478
        return None
 
1479
 
1328
1480
    def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1329
1481
                                         other_branch):
1330
1482
        """Ensure that revision_b is a descendant of revision_a.
1394
1546
        return not (self == other)
1395
1547
 
1396
1548
    @classmethod
1397
 
    def find_format(klass, a_bzrdir):
 
1549
    def find_format(klass, a_bzrdir, name=None):
1398
1550
        """Return the format for the branch object in a_bzrdir."""
1399
1551
        try:
1400
 
            transport = a_bzrdir.get_branch_transport(None)
1401
 
            format_string = transport.get("format").read()
1402
 
            return klass._formats[format_string]
 
1552
            transport = a_bzrdir.get_branch_transport(None, name=name)
 
1553
            format_string = transport.get_bytes("format")
 
1554
            format = klass._formats[format_string]
 
1555
            if isinstance(format, MetaDirBranchFormatFactory):
 
1556
                return format()
 
1557
            return format
1403
1558
        except errors.NoSuchFile:
1404
 
            raise errors.NotBranchError(path=transport.base)
 
1559
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1405
1560
        except KeyError:
1406
1561
            raise errors.UnknownFormatError(format=format_string, kind='branch')
1407
1562
 
1410
1565
        """Return the current default format."""
1411
1566
        return klass._default_format
1412
1567
 
1413
 
    def get_reference(self, a_bzrdir):
 
1568
    @classmethod
 
1569
    def get_formats(klass):
 
1570
        """Get all the known formats.
 
1571
 
 
1572
        Warning: This triggers a load of all lazy registered formats: do not
 
1573
        use except when that is desireed.
 
1574
        """
 
1575
        result = []
 
1576
        for fmt in klass._formats.values():
 
1577
            if isinstance(fmt, MetaDirBranchFormatFactory):
 
1578
                fmt = fmt()
 
1579
            result.append(fmt)
 
1580
        return result
 
1581
 
 
1582
    def get_reference(self, a_bzrdir, name=None):
1414
1583
        """Get the target reference of the branch in a_bzrdir.
1415
1584
 
1416
1585
        format probing must have been completed before calling
1418
1587
        in a_bzrdir is correct.
1419
1588
 
1420
1589
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1590
        :param name: Name of the colocated branch to fetch
1421
1591
        :return: None if the branch is not a reference branch.
1422
1592
        """
1423
1593
        return None
1424
1594
 
1425
1595
    @classmethod
1426
 
    def set_reference(self, a_bzrdir, to_branch):
 
1596
    def set_reference(self, a_bzrdir, name, to_branch):
1427
1597
        """Set the target reference of the branch in a_bzrdir.
1428
1598
 
1429
1599
        format probing must have been completed before calling
1431
1601
        in a_bzrdir is correct.
1432
1602
 
1433
1603
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1604
        :param name: Name of colocated branch to set, None for default
1434
1605
        :param to_branch: branch that the checkout is to reference
1435
1606
        """
1436
1607
        raise NotImplementedError(self.set_reference)
1443
1614
        """Return the short format description for this format."""
1444
1615
        raise NotImplementedError(self.get_format_description)
1445
1616
 
1446
 
    def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
1447
 
                           set_format=True):
 
1617
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
 
1618
        hooks = Branch.hooks['post_branch_init']
 
1619
        if not hooks:
 
1620
            return
 
1621
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
 
1622
        for hook in hooks:
 
1623
            hook(params)
 
1624
 
 
1625
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
1626
                           lock_type='metadir', set_format=True):
1448
1627
        """Initialize a branch in a bzrdir, with specified files
1449
1628
 
1450
1629
        :param a_bzrdir: The bzrdir to initialize the branch in
1451
1630
        :param utf8_files: The files to create as a list of
1452
1631
            (filename, content) tuples
 
1632
        :param name: Name of colocated branch to create, if any
1453
1633
        :param set_format: If True, set the format with
1454
1634
            self.get_format_string.  (BzrBranch4 has its format set
1455
1635
            elsewhere)
1456
1636
        :return: a branch in this format
1457
1637
        """
1458
 
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1459
 
        branch_transport = a_bzrdir.get_branch_transport(self)
 
1638
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
1639
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1460
1640
        lock_map = {
1461
1641
            'metadir': ('lock', lockdir.LockDir),
1462
1642
            'branch4': ('branch-lock', lockable_files.TransportLock),
1483
1663
        finally:
1484
1664
            if lock_taken:
1485
1665
                control_files.unlock()
1486
 
        return self.open(a_bzrdir, _found=True)
 
1666
        branch = self.open(a_bzrdir, name, _found=True)
 
1667
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
1668
        return branch
1487
1669
 
1488
 
    def initialize(self, a_bzrdir):
1489
 
        """Create a branch of this format in a_bzrdir."""
 
1670
    def initialize(self, a_bzrdir, name=None):
 
1671
        """Create a branch of this format in a_bzrdir.
 
1672
        
 
1673
        :param name: Name of the colocated branch to create.
 
1674
        """
1490
1675
        raise NotImplementedError(self.initialize)
1491
1676
 
1492
1677
    def is_supported(self):
1522
1707
        """
1523
1708
        raise NotImplementedError(self.network_name)
1524
1709
 
1525
 
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
 
1710
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1526
1711
        """Return the branch object for a_bzrdir
1527
1712
 
1528
1713
        :param a_bzrdir: A BzrDir that contains a branch.
 
1714
        :param name: Name of colocated branch to open
1529
1715
        :param _found: a private parameter, do not use it. It is used to
1530
1716
            indicate if format probing has already be done.
1531
1717
        :param ignore_fallbacks: when set, no fallback branches will be opened
1535
1721
 
1536
1722
    @classmethod
1537
1723
    def register_format(klass, format):
1538
 
        """Register a metadir format."""
 
1724
        """Register a metadir format.
 
1725
        
 
1726
        See MetaDirBranchFormatFactory for the ability to register a format
 
1727
        without loading the code the format needs until it is actually used.
 
1728
        """
1539
1729
        klass._formats[format.get_format_string()] = format
1540
1730
        # Metadir formats have a network name of their format string, and get
1541
 
        # registered as class factories.
1542
 
        network_format_registry.register(format.get_format_string(), format.__class__)
 
1731
        # registered as factories.
 
1732
        if isinstance(format, MetaDirBranchFormatFactory):
 
1733
            network_format_registry.register(format.get_format_string(), format)
 
1734
        else:
 
1735
            network_format_registry.register(format.get_format_string(),
 
1736
                format.__class__)
1543
1737
 
1544
1738
    @classmethod
1545
1739
    def set_default_format(klass, format):
1565
1759
        return False  # by default
1566
1760
 
1567
1761
 
 
1762
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
 
1763
    """A factory for a BranchFormat object, permitting simple lazy registration.
 
1764
    
 
1765
    While none of the built in BranchFormats are lazy registered yet,
 
1766
    bzrlib.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
 
1767
    use it, and the bzr-loom plugin uses it as well (see
 
1768
    bzrlib.plugins.loom.formats).
 
1769
    """
 
1770
 
 
1771
    def __init__(self, format_string, module_name, member_name):
 
1772
        """Create a MetaDirBranchFormatFactory.
 
1773
 
 
1774
        :param format_string: The format string the format has.
 
1775
        :param module_name: Module to load the format class from.
 
1776
        :param member_name: Attribute name within the module for the format class.
 
1777
        """
 
1778
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
 
1779
        self._format_string = format_string
 
1780
        
 
1781
    def get_format_string(self):
 
1782
        """See BranchFormat.get_format_string."""
 
1783
        return self._format_string
 
1784
 
 
1785
    def __call__(self):
 
1786
        """Used for network_format_registry support."""
 
1787
        return self.get_obj()()
 
1788
 
 
1789
 
1568
1790
class BranchHooks(Hooks):
1569
1791
    """A dictionary mapping hook name to a list of callables for branch hooks.
1570
1792
 
1639
1861
            "multiple hooks installed for transform_fallback_location, "
1640
1862
            "all are called with the url returned from the previous hook."
1641
1863
            "The order is however undefined.", (1, 9), None))
 
1864
        self.create_hook(HookPoint('automatic_tag_name',
 
1865
            "Called to determine an automatic tag name for a revision. "
 
1866
            "automatic_tag_name is called with (branch, revision_id) and "
 
1867
            "should return a tag name or None if no tag name could be "
 
1868
            "determined. The first non-None tag name returned will be used.",
 
1869
            (2, 2), None))
 
1870
        self.create_hook(HookPoint('post_branch_init',
 
1871
            "Called after new branch initialization completes. "
 
1872
            "post_branch_init is called with a "
 
1873
            "bzrlib.branch.BranchInitHookParams. "
 
1874
            "Note that init, branch and checkout (both heavyweight and "
 
1875
            "lightweight) will all trigger this hook.", (2, 2), None))
 
1876
        self.create_hook(HookPoint('post_switch',
 
1877
            "Called after a checkout switches branch. "
 
1878
            "post_switch is called with a "
 
1879
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
 
1880
 
1642
1881
 
1643
1882
 
1644
1883
# install the default hooks into the Branch class.
1683
1922
            self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1684
1923
 
1685
1924
 
 
1925
class BranchInitHookParams(object):
 
1926
    """Object holding parameters passed to *_branch_init hooks.
 
1927
 
 
1928
    There are 4 fields that hooks may wish to access:
 
1929
 
 
1930
    :ivar format: the branch format
 
1931
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
 
1932
    :ivar name: name of colocated branch, if any (or None)
 
1933
    :ivar branch: the branch created
 
1934
 
 
1935
    Note that for lightweight checkouts, the bzrdir and format fields refer to
 
1936
    the checkout, hence they are different from the corresponding fields in
 
1937
    branch, which refer to the original branch.
 
1938
    """
 
1939
 
 
1940
    def __init__(self, format, a_bzrdir, name, branch):
 
1941
        """Create a group of BranchInitHook parameters.
 
1942
 
 
1943
        :param format: the branch format
 
1944
        :param a_bzrdir: the BzrDir where the branch will be/has been
 
1945
            initialized
 
1946
        :param name: name of colocated branch, if any (or None)
 
1947
        :param branch: the branch created
 
1948
 
 
1949
        Note that for lightweight checkouts, the bzrdir and format fields refer
 
1950
        to the checkout, hence they are different from the corresponding fields
 
1951
        in branch, which refer to the original branch.
 
1952
        """
 
1953
        self.format = format
 
1954
        self.bzrdir = a_bzrdir
 
1955
        self.name = name
 
1956
        self.branch = branch
 
1957
 
 
1958
    def __eq__(self, other):
 
1959
        return self.__dict__ == other.__dict__
 
1960
 
 
1961
    def __repr__(self):
 
1962
        return "<%s of %s>" % (self.__class__.__name__, self.branch)
 
1963
 
 
1964
 
 
1965
class SwitchHookParams(object):
 
1966
    """Object holding parameters passed to *_switch hooks.
 
1967
 
 
1968
    There are 4 fields that hooks may wish to access:
 
1969
 
 
1970
    :ivar control_dir: BzrDir of the checkout to change
 
1971
    :ivar to_branch: branch that the checkout is to reference
 
1972
    :ivar force: skip the check for local commits in a heavy checkout
 
1973
    :ivar revision_id: revision ID to switch to (or None)
 
1974
    """
 
1975
 
 
1976
    def __init__(self, control_dir, to_branch, force, revision_id):
 
1977
        """Create a group of SwitchHook parameters.
 
1978
 
 
1979
        :param control_dir: BzrDir of the checkout to change
 
1980
        :param to_branch: branch that the checkout is to reference
 
1981
        :param force: skip the check for local commits in a heavy checkout
 
1982
        :param revision_id: revision ID to switch to (or None)
 
1983
        """
 
1984
        self.control_dir = control_dir
 
1985
        self.to_branch = to_branch
 
1986
        self.force = force
 
1987
        self.revision_id = revision_id
 
1988
 
 
1989
    def __eq__(self, other):
 
1990
        return self.__dict__ == other.__dict__
 
1991
 
 
1992
    def __repr__(self):
 
1993
        return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
 
1994
            self.control_dir, self.to_branch,
 
1995
            self.revision_id)
 
1996
 
 
1997
 
1686
1998
class BzrBranchFormat4(BranchFormat):
1687
1999
    """Bzr branch format 4.
1688
2000
 
1695
2007
        """See BranchFormat.get_format_description()."""
1696
2008
        return "Branch format 4"
1697
2009
 
1698
 
    def initialize(self, a_bzrdir):
 
2010
    def initialize(self, a_bzrdir, name=None):
1699
2011
        """Create a branch of this format in a_bzrdir."""
1700
2012
        utf8_files = [('revision-history', ''),
1701
2013
                      ('branch-name', ''),
1702
2014
                      ]
1703
 
        return self._initialize_helper(a_bzrdir, utf8_files,
 
2015
        return self._initialize_helper(a_bzrdir, utf8_files, name=name,
1704
2016
                                       lock_type='branch4', set_format=False)
1705
2017
 
1706
2018
    def __init__(self):
1711
2023
        """The network name for this format is the control dirs disk label."""
1712
2024
        return self._matchingbzrdir.get_format_string()
1713
2025
 
1714
 
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
 
2026
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1715
2027
        """See BranchFormat.open()."""
1716
2028
        if not _found:
1717
2029
            # we are being called directly and must probe.
1719
2031
        return BzrBranch(_format=self,
1720
2032
                         _control_files=a_bzrdir._control_files,
1721
2033
                         a_bzrdir=a_bzrdir,
 
2034
                         name=name,
1722
2035
                         _repository=a_bzrdir.open_repository())
1723
2036
 
1724
2037
    def __str__(self):
1739
2052
        """
1740
2053
        return self.get_format_string()
1741
2054
 
1742
 
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
 
2055
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1743
2056
        """See BranchFormat.open()."""
1744
2057
        if not _found:
1745
 
            format = BranchFormat.find_format(a_bzrdir)
 
2058
            format = BranchFormat.find_format(a_bzrdir, name=name)
1746
2059
            if format.__class__ != self.__class__:
1747
2060
                raise AssertionError("wrong format %r found for %r" %
1748
2061
                    (format, self))
 
2062
        transport = a_bzrdir.get_branch_transport(None, name=name)
1749
2063
        try:
1750
 
            transport = a_bzrdir.get_branch_transport(None)
1751
2064
            control_files = lockable_files.LockableFiles(transport, 'lock',
1752
2065
                                                         lockdir.LockDir)
1753
2066
            return self._branch_class()(_format=self,
1754
2067
                              _control_files=control_files,
 
2068
                              name=name,
1755
2069
                              a_bzrdir=a_bzrdir,
1756
2070
                              _repository=a_bzrdir.find_repository(),
1757
2071
                              ignore_fallbacks=ignore_fallbacks)
1758
2072
        except errors.NoSuchFile:
1759
 
            raise errors.NotBranchError(path=transport.base)
 
2073
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1760
2074
 
1761
2075
    def __init__(self):
1762
2076
        super(BranchFormatMetadir, self).__init__()
1791
2105
        """See BranchFormat.get_format_description()."""
1792
2106
        return "Branch format 5"
1793
2107
 
1794
 
    def initialize(self, a_bzrdir):
 
2108
    def initialize(self, a_bzrdir, name=None):
1795
2109
        """Create a branch of this format in a_bzrdir."""
1796
2110
        utf8_files = [('revision-history', ''),
1797
2111
                      ('branch-name', ''),
1798
2112
                      ]
1799
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
2113
        return self._initialize_helper(a_bzrdir, utf8_files, name)
1800
2114
 
1801
2115
    def supports_tags(self):
1802
2116
        return False
1824
2138
        """See BranchFormat.get_format_description()."""
1825
2139
        return "Branch format 6"
1826
2140
 
1827
 
    def initialize(self, a_bzrdir):
 
2141
    def initialize(self, a_bzrdir, name=None):
1828
2142
        """Create a branch of this format in a_bzrdir."""
1829
2143
        utf8_files = [('last-revision', '0 null:\n'),
1830
2144
                      ('branch.conf', ''),
1831
2145
                      ('tags', ''),
1832
2146
                      ]
1833
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
2147
        return self._initialize_helper(a_bzrdir, utf8_files, name)
1834
2148
 
1835
2149
    def make_tags(self, branch):
1836
2150
        """See bzrlib.branch.BranchFormat.make_tags()."""
1854
2168
        """See BranchFormat.get_format_description()."""
1855
2169
        return "Branch format 8"
1856
2170
 
1857
 
    def initialize(self, a_bzrdir):
 
2171
    def initialize(self, a_bzrdir, name=None):
1858
2172
        """Create a branch of this format in a_bzrdir."""
1859
2173
        utf8_files = [('last-revision', '0 null:\n'),
1860
2174
                      ('branch.conf', ''),
1861
2175
                      ('tags', ''),
1862
2176
                      ('references', '')
1863
2177
                      ]
1864
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
2178
        return self._initialize_helper(a_bzrdir, utf8_files, name)
1865
2179
 
1866
2180
    def __init__(self):
1867
2181
        super(BzrBranchFormat8, self).__init__()
1890
2204
    This format was introduced in bzr 1.6.
1891
2205
    """
1892
2206
 
1893
 
    def initialize(self, a_bzrdir):
 
2207
    def initialize(self, a_bzrdir, name=None):
1894
2208
        """Create a branch of this format in a_bzrdir."""
1895
2209
        utf8_files = [('last-revision', '0 null:\n'),
1896
2210
                      ('branch.conf', ''),
1897
2211
                      ('tags', ''),
1898
2212
                      ]
1899
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
2213
        return self._initialize_helper(a_bzrdir, utf8_files, name)
1900
2214
 
1901
2215
    def _branch_class(self):
1902
2216
        return BzrBranch7
1934
2248
        """See BranchFormat.get_format_description()."""
1935
2249
        return "Checkout reference format 1"
1936
2250
 
1937
 
    def get_reference(self, a_bzrdir):
 
2251
    def get_reference(self, a_bzrdir, name=None):
1938
2252
        """See BranchFormat.get_reference()."""
1939
 
        transport = a_bzrdir.get_branch_transport(None)
1940
 
        return transport.get('location').read()
 
2253
        transport = a_bzrdir.get_branch_transport(None, name=name)
 
2254
        return transport.get_bytes('location')
1941
2255
 
1942
 
    def set_reference(self, a_bzrdir, to_branch):
 
2256
    def set_reference(self, a_bzrdir, name, to_branch):
1943
2257
        """See BranchFormat.set_reference()."""
1944
 
        transport = a_bzrdir.get_branch_transport(None)
 
2258
        transport = a_bzrdir.get_branch_transport(None, name=name)
1945
2259
        location = transport.put_bytes('location', to_branch.base)
1946
2260
 
1947
 
    def initialize(self, a_bzrdir, target_branch=None):
 
2261
    def initialize(self, a_bzrdir, name=None, target_branch=None):
1948
2262
        """Create a branch of this format in a_bzrdir."""
1949
2263
        if target_branch is None:
1950
2264
            # this format does not implement branch itself, thus the implicit
1951
2265
            # creation contract must see it as uninitializable
1952
2266
            raise errors.UninitializableFormat(self)
1953
 
        mutter('creating branch reference in %s', a_bzrdir.transport.base)
1954
 
        branch_transport = a_bzrdir.get_branch_transport(self)
 
2267
        mutter('creating branch reference in %s', a_bzrdir.user_url)
 
2268
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1955
2269
        branch_transport.put_bytes('location',
1956
 
            target_branch.bzrdir.root_transport.base)
 
2270
            target_branch.bzrdir.user_url)
1957
2271
        branch_transport.put_bytes('format', self.get_format_string())
1958
 
        return self.open(
1959
 
            a_bzrdir, _found=True,
 
2272
        branch = self.open(
 
2273
            a_bzrdir, name, _found=True,
1960
2274
            possible_transports=[target_branch.bzrdir.root_transport])
 
2275
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
2276
        return branch
1961
2277
 
1962
2278
    def __init__(self):
1963
2279
        super(BranchReferenceFormat, self).__init__()
1969
2285
        def clone(to_bzrdir, revision_id=None,
1970
2286
            repository_policy=None):
1971
2287
            """See Branch.clone()."""
1972
 
            return format.initialize(to_bzrdir, a_branch)
 
2288
            return format.initialize(to_bzrdir, target_branch=a_branch)
1973
2289
            # cannot obey revision_id limits when cloning a reference ...
1974
2290
            # FIXME RBC 20060210 either nuke revision_id for clone, or
1975
2291
            # emit some sort of warning/error to the caller ?!
1976
2292
        return clone
1977
2293
 
1978
 
    def open(self, a_bzrdir, _found=False, location=None,
 
2294
    def open(self, a_bzrdir, name=None, _found=False, location=None,
1979
2295
             possible_transports=None, ignore_fallbacks=False):
1980
2296
        """Return the branch that the branch reference in a_bzrdir points at.
1981
2297
 
1982
2298
        :param a_bzrdir: A BzrDir that contains a branch.
 
2299
        :param name: Name of colocated branch to open, if any
1983
2300
        :param _found: a private parameter, do not use it. It is used to
1984
2301
            indicate if format probing has already be done.
1985
2302
        :param ignore_fallbacks: when set, no fallback branches will be opened
1990
2307
        :param possible_transports: An optional reusable transports list.
1991
2308
        """
1992
2309
        if not _found:
1993
 
            format = BranchFormat.find_format(a_bzrdir)
 
2310
            format = BranchFormat.find_format(a_bzrdir, name=name)
1994
2311
            if format.__class__ != self.__class__:
1995
2312
                raise AssertionError("wrong format %r found for %r" %
1996
2313
                    (format, self))
1997
2314
        if location is None:
1998
 
            location = self.get_reference(a_bzrdir)
 
2315
            location = self.get_reference(a_bzrdir, name)
1999
2316
        real_bzrdir = bzrdir.BzrDir.open(
2000
2317
            location, possible_transports=possible_transports)
2001
 
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
 
2318
        result = real_bzrdir.open_branch(name=name, 
 
2319
            ignore_fallbacks=ignore_fallbacks)
2002
2320
        # this changes the behaviour of result.clone to create a new reference
2003
2321
        # rather than a copy of the content of the branch.
2004
2322
        # I did not use a proxy object because that needs much more extensive
2031
2349
BranchFormat.register_format(__format6)
2032
2350
BranchFormat.register_format(__format7)
2033
2351
BranchFormat.register_format(__format8)
2034
 
BranchFormat.set_default_format(__format6)
 
2352
BranchFormat.set_default_format(__format7)
2035
2353
_legacy_formats = [BzrBranchFormat4(),
2036
2354
    ]
2037
2355
network_format_registry.register(
2038
2356
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2039
2357
 
2040
2358
 
2041
 
class BzrBranch(Branch):
 
2359
class BranchWriteLockResult(LogicalLockResult):
 
2360
    """The result of write locking a branch.
 
2361
 
 
2362
    :ivar branch_token: The token obtained from the underlying branch lock, or
 
2363
        None.
 
2364
    :ivar unlock: A callable which will unlock the lock.
 
2365
    """
 
2366
 
 
2367
    def __init__(self, unlock, branch_token):
 
2368
        LogicalLockResult.__init__(self, unlock)
 
2369
        self.branch_token = branch_token
 
2370
 
 
2371
    def __repr__(self):
 
2372
        return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
 
2373
            self.unlock)
 
2374
 
 
2375
 
 
2376
class BzrBranch(Branch, _RelockDebugMixin):
2042
2377
    """A branch stored in the actual filesystem.
2043
2378
 
2044
2379
    Note that it's "local" in the context of the filesystem; it doesn't
2050
2385
    :ivar repository: Repository for this branch.
2051
2386
    :ivar base: The url of the base directory for this branch; the one
2052
2387
        containing the .bzr directory.
 
2388
    :ivar name: Optional colocated branch name as it exists in the control
 
2389
        directory.
2053
2390
    """
2054
2391
 
2055
2392
    def __init__(self, _format=None,
2056
 
                 _control_files=None, a_bzrdir=None, _repository=None,
2057
 
                 ignore_fallbacks=False):
 
2393
                 _control_files=None, a_bzrdir=None, name=None,
 
2394
                 _repository=None, ignore_fallbacks=False):
2058
2395
        """Create new branch object at a particular location."""
2059
2396
        if a_bzrdir is None:
2060
2397
            raise ValueError('a_bzrdir must be supplied')
2061
2398
        else:
2062
2399
            self.bzrdir = a_bzrdir
2063
2400
        self._base = self.bzrdir.transport.clone('..').base
 
2401
        self.name = name
2064
2402
        # XXX: We should be able to just do
2065
2403
        #   self.base = self.bzrdir.root_transport.base
2066
2404
        # but this does not quite work yet -- mbp 20080522
2073
2411
        Branch.__init__(self)
2074
2412
 
2075
2413
    def __str__(self):
2076
 
        return '%s(%r)' % (self.__class__.__name__, self.base)
 
2414
        if self.name is None:
 
2415
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
 
2416
        else:
 
2417
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
 
2418
                self.name)
2077
2419
 
2078
2420
    __repr__ = __str__
2079
2421
 
2090
2432
        return self.control_files.is_locked()
2091
2433
 
2092
2434
    def lock_write(self, token=None):
 
2435
        """Lock the branch for write operations.
 
2436
 
 
2437
        :param token: A token to permit reacquiring a previously held and
 
2438
            preserved lock.
 
2439
        :return: A BranchWriteLockResult.
 
2440
        """
 
2441
        if not self.is_locked():
 
2442
            self._note_lock('w')
2093
2443
        # All-in-one needs to always unlock/lock.
2094
2444
        repo_control = getattr(self.repository, 'control_files', None)
2095
2445
        if self.control_files == repo_control or not self.is_locked():
 
2446
            self.repository._warn_if_deprecated(self)
2096
2447
            self.repository.lock_write()
2097
2448
            took_lock = True
2098
2449
        else:
2099
2450
            took_lock = False
2100
2451
        try:
2101
 
            return self.control_files.lock_write(token=token)
 
2452
            return BranchWriteLockResult(self.unlock,
 
2453
                self.control_files.lock_write(token=token))
2102
2454
        except:
2103
2455
            if took_lock:
2104
2456
                self.repository.unlock()
2105
2457
            raise
2106
2458
 
2107
2459
    def lock_read(self):
 
2460
        """Lock the branch for read operations.
 
2461
 
 
2462
        :return: A bzrlib.lock.LogicalLockResult.
 
2463
        """
 
2464
        if not self.is_locked():
 
2465
            self._note_lock('r')
2108
2466
        # All-in-one needs to always unlock/lock.
2109
2467
        repo_control = getattr(self.repository, 'control_files', None)
2110
2468
        if self.control_files == repo_control or not self.is_locked():
 
2469
            self.repository._warn_if_deprecated(self)
2111
2470
            self.repository.lock_read()
2112
2471
            took_lock = True
2113
2472
        else:
2114
2473
            took_lock = False
2115
2474
        try:
2116
2475
            self.control_files.lock_read()
 
2476
            return LogicalLockResult(self.unlock)
2117
2477
        except:
2118
2478
            if took_lock:
2119
2479
                self.repository.unlock()
2120
2480
            raise
2121
2481
 
 
2482
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2122
2483
    def unlock(self):
2123
2484
        try:
2124
2485
            self.control_files.unlock()
2287
2648
        return result
2288
2649
 
2289
2650
    def get_stacked_on_url(self):
2290
 
        raise errors.UnstackableBranchFormat(self._format, self.base)
 
2651
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2291
2652
 
2292
2653
    def set_push_location(self, location):
2293
2654
        """See Branch.set_push_location."""
2483
2844
        if _mod_revision.is_null(last_revision):
2484
2845
            return
2485
2846
        if last_revision not in self._lefthand_history(revision_id):
2486
 
            raise errors.AppendRevisionsOnlyViolation(self.base)
 
2847
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
2487
2848
 
2488
2849
    def _gen_revision_history(self):
2489
2850
        """Generate the revision history from last revision
2589
2950
        if branch_location is None:
2590
2951
            return Branch.reference_parent(self, file_id, path,
2591
2952
                                           possible_transports)
2592
 
        branch_location = urlutils.join(self.base, branch_location)
 
2953
        branch_location = urlutils.join(self.user_url, branch_location)
2593
2954
        return Branch.open(branch_location,
2594
2955
                           possible_transports=possible_transports)
2595
2956
 
2641
3002
        return stacked_url
2642
3003
 
2643
3004
    def _get_append_revisions_only(self):
2644
 
        value = self.get_config().get_user_option('append_revisions_only')
2645
 
        return value == 'True'
 
3005
        return self.get_config(
 
3006
            ).get_user_option_as_bool('append_revisions_only')
2646
3007
 
2647
3008
    @needs_write_lock
2648
3009
    def generate_revision_history(self, revision_id, last_rev=None,
2710
3071
    """
2711
3072
 
2712
3073
    def get_stacked_on_url(self):
2713
 
        raise errors.UnstackableBranchFormat(self._format, self.base)
 
3074
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2714
3075
 
2715
3076
 
2716
3077
######################################################################
2795
3156
 
2796
3157
    def __init__(self, branch):
2797
3158
        self.branch = branch
2798
 
        self.ghosts_in_mainline = False
 
3159
        self.errors = []
2799
3160
 
2800
3161
    def report_results(self, verbose):
2801
3162
        """Report the check results via trace.note.
2803
3164
        :param verbose: Requests more detailed display of what was checked,
2804
3165
            if any.
2805
3166
        """
2806
 
        note('checked branch %s format %s',
2807
 
             self.branch.base,
2808
 
             self.branch._format)
2809
 
        if self.ghosts_in_mainline:
2810
 
            note('branch contains ghosts in mainline')
 
3167
        note('checked branch %s format %s', self.branch.user_url,
 
3168
            self.branch._format)
 
3169
        for error in self.errors:
 
3170
            note('found error:%s', error)
2811
3171
 
2812
3172
 
2813
3173
class Converter5to6(object):
2905
3265
    _optimisers = []
2906
3266
    """The available optimised InterBranch types."""
2907
3267
 
2908
 
    @staticmethod
2909
 
    def _get_branch_formats_to_test():
2910
 
        """Return a tuple with the Branch formats to use when testing."""
2911
 
        raise NotImplementedError(InterBranch._get_branch_formats_to_test)
 
3268
    @classmethod
 
3269
    def _get_branch_formats_to_test(klass):
 
3270
        """Return an iterable of format tuples for testing.
 
3271
        
 
3272
        :return: An iterable of (from_format, to_format) to use when testing
 
3273
            this InterBranch class. Each InterBranch class should define this
 
3274
            method itself.
 
3275
        """
 
3276
        raise NotImplementedError(klass._get_branch_formats_to_test)
2912
3277
 
 
3278
    @needs_write_lock
2913
3279
    def pull(self, overwrite=False, stop_revision=None,
2914
3280
             possible_transports=None, local=False):
2915
3281
        """Mirror source into target branch.
2920
3286
        """
2921
3287
        raise NotImplementedError(self.pull)
2922
3288
 
 
3289
    @needs_write_lock
2923
3290
    def update_revisions(self, stop_revision=None, overwrite=False,
2924
3291
                         graph=None):
2925
3292
        """Pull in new perfect-fit revisions.
2933
3300
        """
2934
3301
        raise NotImplementedError(self.update_revisions)
2935
3302
 
 
3303
    @needs_write_lock
2936
3304
    def push(self, overwrite=False, stop_revision=None,
2937
3305
             _override_hook_source_branch=None):
2938
3306
        """Mirror the source branch into the target branch.
2941
3309
        """
2942
3310
        raise NotImplementedError(self.push)
2943
3311
 
 
3312
    @needs_write_lock
 
3313
    def copy_content_into(self, revision_id=None):
 
3314
        """Copy the content of source into target
 
3315
 
 
3316
        revision_id: if not None, the revision history in the new branch will
 
3317
                     be truncated to end with revision_id.
 
3318
        """
 
3319
        raise NotImplementedError(self.copy_content_into)
 
3320
 
2944
3321
 
2945
3322
class GenericInterBranch(InterBranch):
2946
 
    """InterBranch implementation that uses public Branch functions.
2947
 
    """
2948
 
 
2949
 
    @staticmethod
2950
 
    def _get_branch_formats_to_test():
2951
 
        return BranchFormat._default_format, BranchFormat._default_format
2952
 
 
 
3323
    """InterBranch implementation that uses public Branch functions."""
 
3324
 
 
3325
    @classmethod
 
3326
    def is_compatible(klass, source, target):
 
3327
        # GenericBranch uses the public API, so always compatible
 
3328
        return True
 
3329
 
 
3330
    @classmethod
 
3331
    def _get_branch_formats_to_test(klass):
 
3332
        return [(BranchFormat._default_format, BranchFormat._default_format)]
 
3333
 
 
3334
    @classmethod
 
3335
    def unwrap_format(klass, format):
 
3336
        if isinstance(format, remote.RemoteBranchFormat):
 
3337
            format._ensure_real()
 
3338
            return format._custom_format
 
3339
        return format                                                                                                  
 
3340
 
 
3341
    @needs_write_lock
 
3342
    def copy_content_into(self, revision_id=None):
 
3343
        """Copy the content of source into target
 
3344
 
 
3345
        revision_id: if not None, the revision history in the new branch will
 
3346
                     be truncated to end with revision_id.
 
3347
        """
 
3348
        self.source.update_references(self.target)
 
3349
        self.source._synchronize_history(self.target, revision_id)
 
3350
        try:
 
3351
            parent = self.source.get_parent()
 
3352
        except errors.InaccessibleParent, e:
 
3353
            mutter('parent was not accessible to copy: %s', e)
 
3354
        else:
 
3355
            if parent:
 
3356
                self.target.set_parent(parent)
 
3357
        if self.source._push_should_merge_tags():
 
3358
            self.source.tags.merge_to(self.target.tags)
 
3359
 
 
3360
    @needs_write_lock
2953
3361
    def update_revisions(self, stop_revision=None, overwrite=False,
2954
3362
        graph=None):
2955
3363
        """See InterBranch.update_revisions()."""
2956
 
        self.source.lock_read()
2957
 
        try:
2958
 
            other_revno, other_last_revision = self.source.last_revision_info()
2959
 
            stop_revno = None # unknown
2960
 
            if stop_revision is None:
2961
 
                stop_revision = other_last_revision
2962
 
                if _mod_revision.is_null(stop_revision):
2963
 
                    # if there are no commits, we're done.
2964
 
                    return
2965
 
                stop_revno = other_revno
2966
 
 
2967
 
            # what's the current last revision, before we fetch [and change it
2968
 
            # possibly]
2969
 
            last_rev = _mod_revision.ensure_null(self.target.last_revision())
2970
 
            # we fetch here so that we don't process data twice in the common
2971
 
            # case of having something to pull, and so that the check for
2972
 
            # already merged can operate on the just fetched graph, which will
2973
 
            # be cached in memory.
2974
 
            self.target.fetch(self.source, stop_revision)
2975
 
            # Check to see if one is an ancestor of the other
2976
 
            if not overwrite:
2977
 
                if graph is None:
2978
 
                    graph = self.target.repository.get_graph()
2979
 
                if self.target._check_if_descendant_or_diverged(
2980
 
                        stop_revision, last_rev, graph, self.source):
2981
 
                    # stop_revision is a descendant of last_rev, but we aren't
2982
 
                    # overwriting, so we're done.
2983
 
                    return
2984
 
            if stop_revno is None:
2985
 
                if graph is None:
2986
 
                    graph = self.target.repository.get_graph()
2987
 
                this_revno, this_last_revision = \
2988
 
                        self.target.last_revision_info()
2989
 
                stop_revno = graph.find_distance_to_null(stop_revision,
2990
 
                                [(other_last_revision, other_revno),
2991
 
                                 (this_last_revision, this_revno)])
2992
 
            self.target.set_last_revision_info(stop_revno, stop_revision)
2993
 
        finally:
2994
 
            self.source.unlock()
2995
 
 
 
3364
        other_revno, other_last_revision = self.source.last_revision_info()
 
3365
        stop_revno = None # unknown
 
3366
        if stop_revision is None:
 
3367
            stop_revision = other_last_revision
 
3368
            if _mod_revision.is_null(stop_revision):
 
3369
                # if there are no commits, we're done.
 
3370
                return
 
3371
            stop_revno = other_revno
 
3372
 
 
3373
        # what's the current last revision, before we fetch [and change it
 
3374
        # possibly]
 
3375
        last_rev = _mod_revision.ensure_null(self.target.last_revision())
 
3376
        # we fetch here so that we don't process data twice in the common
 
3377
        # case of having something to pull, and so that the check for
 
3378
        # already merged can operate on the just fetched graph, which will
 
3379
        # be cached in memory.
 
3380
        self.target.fetch(self.source, stop_revision)
 
3381
        # Check to see if one is an ancestor of the other
 
3382
        if not overwrite:
 
3383
            if graph is None:
 
3384
                graph = self.target.repository.get_graph()
 
3385
            if self.target._check_if_descendant_or_diverged(
 
3386
                    stop_revision, last_rev, graph, self.source):
 
3387
                # stop_revision is a descendant of last_rev, but we aren't
 
3388
                # overwriting, so we're done.
 
3389
                return
 
3390
        if stop_revno is None:
 
3391
            if graph is None:
 
3392
                graph = self.target.repository.get_graph()
 
3393
            this_revno, this_last_revision = \
 
3394
                    self.target.last_revision_info()
 
3395
            stop_revno = graph.find_distance_to_null(stop_revision,
 
3396
                            [(other_last_revision, other_revno),
 
3397
                             (this_last_revision, this_revno)])
 
3398
        self.target.set_last_revision_info(stop_revno, stop_revision)
 
3399
 
 
3400
    @needs_write_lock
2996
3401
    def pull(self, overwrite=False, stop_revision=None,
2997
 
             possible_transports=None, _hook_master=None, run_hooks=True,
 
3402
             possible_transports=None, run_hooks=True,
2998
3403
             _override_hook_target=None, local=False):
2999
 
        """See Branch.pull.
 
3404
        """Pull from source into self, updating my master if any.
3000
3405
 
3001
 
        :param _hook_master: Private parameter - set the branch to
3002
 
            be supplied as the master to pull hooks.
3003
3406
        :param run_hooks: Private parameter - if false, this branch
3004
3407
            is being called because it's the master of the primary branch,
3005
3408
            so it should not run its hooks.
3006
 
        :param _override_hook_target: Private parameter - set the branch to be
3007
 
            supplied as the target_branch to pull hooks.
3008
 
        :param local: Only update the local branch, and not the bound branch.
3009
3409
        """
3010
 
        # This type of branch can't be bound.
3011
 
        if local:
 
3410
        bound_location = self.target.get_bound_location()
 
3411
        if local and not bound_location:
3012
3412
            raise errors.LocalRequiresBoundBranch()
3013
 
        result = PullResult()
3014
 
        result.source_branch = self.source
3015
 
        if _override_hook_target is None:
3016
 
            result.target_branch = self.target
3017
 
        else:
3018
 
            result.target_branch = _override_hook_target
3019
 
        self.source.lock_read()
 
3413
        master_branch = None
 
3414
        if not local and bound_location and self.source.user_url != bound_location:
 
3415
            # not pulling from master, so we need to update master.
 
3416
            master_branch = self.target.get_master_branch(possible_transports)
 
3417
            master_branch.lock_write()
3020
3418
        try:
3021
 
            # We assume that during 'pull' the target repository is closer than
3022
 
            # the source one.
3023
 
            self.source.update_references(self.target)
3024
 
            graph = self.target.repository.get_graph(self.source.repository)
3025
 
            # TODO: Branch formats should have a flag that indicates 
3026
 
            # that revno's are expensive, and pull() should honor that flag.
3027
 
            # -- JRV20090506
3028
 
            result.old_revno, result.old_revid = \
3029
 
                self.target.last_revision_info()
3030
 
            self.target.update_revisions(self.source, stop_revision,
3031
 
                overwrite=overwrite, graph=graph)
3032
 
            # TODO: The old revid should be specified when merging tags, 
3033
 
            # so a tags implementation that versions tags can only 
3034
 
            # pull in the most recent changes. -- JRV20090506
3035
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3036
 
                overwrite)
3037
 
            result.new_revno, result.new_revid = self.target.last_revision_info()
3038
 
            if _hook_master:
3039
 
                result.master_branch = _hook_master
3040
 
                result.local_branch = result.target_branch
3041
 
            else:
3042
 
                result.master_branch = result.target_branch
3043
 
                result.local_branch = None
3044
 
            if run_hooks:
3045
 
                for hook in Branch.hooks['post_pull']:
3046
 
                    hook(result)
 
3419
            if master_branch:
 
3420
                # pull from source into master.
 
3421
                master_branch.pull(self.source, overwrite, stop_revision,
 
3422
                    run_hooks=False)
 
3423
            return self._pull(overwrite,
 
3424
                stop_revision, _hook_master=master_branch,
 
3425
                run_hooks=run_hooks,
 
3426
                _override_hook_target=_override_hook_target)
3047
3427
        finally:
3048
 
            self.source.unlock()
3049
 
        return result
 
3428
            if master_branch:
 
3429
                master_branch.unlock()
3050
3430
 
3051
3431
    def push(self, overwrite=False, stop_revision=None,
3052
3432
             _override_hook_source_branch=None):
3114
3494
            _run_hooks()
3115
3495
            return result
3116
3496
 
3117
 
    @classmethod
3118
 
    def is_compatible(self, source, target):
3119
 
        # GenericBranch uses the public API, so always compatible
3120
 
        return True
3121
 
 
3122
 
 
3123
 
class InterToBranch5(GenericInterBranch):
3124
 
 
3125
 
    @staticmethod
3126
 
    def _get_branch_formats_to_test():
3127
 
        return BranchFormat._default_format, BzrBranchFormat5()
3128
 
 
3129
 
    def pull(self, overwrite=False, stop_revision=None,
3130
 
             possible_transports=None, run_hooks=True,
 
3497
    def _pull(self, overwrite=False, stop_revision=None,
 
3498
             possible_transports=None, _hook_master=None, run_hooks=True,
3131
3499
             _override_hook_target=None, local=False):
3132
 
        """Pull from source into self, updating my master if any.
3133
 
 
 
3500
        """See Branch.pull.
 
3501
 
 
3502
        This function is the core worker, used by GenericInterBranch.pull to
 
3503
        avoid duplication when pulling source->master and source->local.
 
3504
 
 
3505
        :param _hook_master: Private parameter - set the branch to
 
3506
            be supplied as the master to pull hooks.
3134
3507
        :param run_hooks: Private parameter - if false, this branch
3135
3508
            is being called because it's the master of the primary branch,
3136
3509
            so it should not run its hooks.
 
3510
        :param _override_hook_target: Private parameter - set the branch to be
 
3511
            supplied as the target_branch to pull hooks.
 
3512
        :param local: Only update the local branch, and not the bound branch.
3137
3513
        """
3138
 
        bound_location = self.target.get_bound_location()
3139
 
        if local and not bound_location:
 
3514
        # This type of branch can't be bound.
 
3515
        if local:
3140
3516
            raise errors.LocalRequiresBoundBranch()
3141
 
        master_branch = None
3142
 
        if not local and bound_location and self.source.base != bound_location:
3143
 
            # not pulling from master, so we need to update master.
3144
 
            master_branch = self.target.get_master_branch(possible_transports)
3145
 
            master_branch.lock_write()
 
3517
        result = PullResult()
 
3518
        result.source_branch = self.source
 
3519
        if _override_hook_target is None:
 
3520
            result.target_branch = self.target
 
3521
        else:
 
3522
            result.target_branch = _override_hook_target
 
3523
        self.source.lock_read()
3146
3524
        try:
3147
 
            if master_branch:
3148
 
                # pull from source into master.
3149
 
                master_branch.pull(self.source, overwrite, stop_revision,
3150
 
                    run_hooks=False)
3151
 
            return super(InterToBranch5, self).pull(overwrite,
3152
 
                stop_revision, _hook_master=master_branch,
3153
 
                run_hooks=run_hooks,
3154
 
                _override_hook_target=_override_hook_target)
 
3525
            # We assume that during 'pull' the target repository is closer than
 
3526
            # the source one.
 
3527
            self.source.update_references(self.target)
 
3528
            graph = self.target.repository.get_graph(self.source.repository)
 
3529
            # TODO: Branch formats should have a flag that indicates 
 
3530
            # that revno's are expensive, and pull() should honor that flag.
 
3531
            # -- JRV20090506
 
3532
            result.old_revno, result.old_revid = \
 
3533
                self.target.last_revision_info()
 
3534
            self.target.update_revisions(self.source, stop_revision,
 
3535
                overwrite=overwrite, graph=graph)
 
3536
            # TODO: The old revid should be specified when merging tags, 
 
3537
            # so a tags implementation that versions tags can only 
 
3538
            # pull in the most recent changes. -- JRV20090506
 
3539
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3540
                overwrite)
 
3541
            result.new_revno, result.new_revid = self.target.last_revision_info()
 
3542
            if _hook_master:
 
3543
                result.master_branch = _hook_master
 
3544
                result.local_branch = result.target_branch
 
3545
            else:
 
3546
                result.master_branch = result.target_branch
 
3547
                result.local_branch = None
 
3548
            if run_hooks:
 
3549
                for hook in Branch.hooks['post_pull']:
 
3550
                    hook(result)
3155
3551
        finally:
3156
 
            if master_branch:
3157
 
                master_branch.unlock()
 
3552
            self.source.unlock()
 
3553
        return result
3158
3554
 
3159
3555
 
3160
3556
InterBranch.register_optimiser(GenericInterBranch)
3161
 
InterBranch.register_optimiser(InterToBranch5)