~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Jelmer Vernooij
  • Date: 2010-12-20 11:57:14 UTC
  • mto: This revision was merged to the branch mainline in revision 5577.
  • Revision ID: jelmer@samba.org-20101220115714-2ru3hfappjweeg7q
Don't use no-plugins.

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,
49
51
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
50
52
from bzrlib.hooks import HookPoint, Hooks
51
53
from bzrlib.inter import InterObject
52
 
from bzrlib.lock import _RelockDebugMixin
 
54
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
53
55
from bzrlib import registry
54
56
from bzrlib.symbol_versioning import (
55
57
    deprecated_in,
63
65
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
64
66
 
65
67
 
66
 
# TODO: Maybe include checks for common corruption of newlines, etc?
67
 
 
68
 
# TODO: Some operations like log might retrieve the same revisions
69
 
# repeatedly to calculate deltas.  We could perhaps have a weakref
70
 
# cache in memory to make this faster.  In general anything can be
71
 
# cached in memory between lock and unlock operations. .. nb thats
72
 
# what the transaction identity map provides
73
 
 
74
 
 
75
 
######################################################################
76
 
# branch objects
77
 
 
78
 
class Branch(object):
 
68
class Branch(controldir.ControlComponent):
79
69
    """Branch holding a history of revisions.
80
70
 
81
 
    base
82
 
        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.
83
74
 
84
75
    hooks: An instance of BranchHooks.
85
76
    """
87
78
    # - RBC 20060112
88
79
    base = None
89
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
 
90
89
    def __init__(self, *ignored, **ignored_too):
91
90
        self.tags = self._format.make_tags(self)
92
91
        self._revision_history_cache = None
93
92
        self._revision_id_to_revno_cache = None
94
93
        self._partial_revision_id_to_revno_cache = {}
95
94
        self._partial_revision_history_cache = []
 
95
        self._tags_bytes = None
96
96
        self._last_revision_info_cache = None
97
97
        self._merge_sorted_revisions_cache = None
98
98
        self._open_hook()
105
105
 
106
106
    def _activate_fallback_location(self, url):
107
107
        """Activate the branch/repository from url as a fallback repository."""
 
108
        for existing_fallback_repo in self.repository._fallback_repositories:
 
109
            if existing_fallback_repo.user_url == url:
 
110
                # This fallback is already configured.  This probably only
 
111
                # happens because BzrDir.sprout is a horrible mess.  To avoid
 
112
                # confusing _unstack we don't add this a second time.
 
113
                mutter('duplicate activation of fallback %r on %r', url, self)
 
114
                return
108
115
        repo = self._get_fallback_repository(url)
109
116
        if repo.has_same_location(self.repository):
110
 
            raise errors.UnstackableLocationError(self.base, url)
 
117
            raise errors.UnstackableLocationError(self.user_url, url)
111
118
        self.repository.add_fallback_repository(repo)
112
119
 
113
120
    def break_lock(self):
167
174
        """
168
175
        control = bzrdir.BzrDir.open(base, _unsupported,
169
176
                                     possible_transports=possible_transports)
170
 
        return control.open_branch(_unsupported)
 
177
        return control.open_branch(unsupported=_unsupported)
171
178
 
172
179
    @staticmethod
173
 
    def open_from_transport(transport, _unsupported=False):
 
180
    def open_from_transport(transport, name=None, _unsupported=False):
174
181
        """Open the branch rooted at transport"""
175
182
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
176
 
        return control.open_branch(_unsupported)
 
183
        return control.open_branch(name=name, unsupported=_unsupported)
177
184
 
178
185
    @staticmethod
179
186
    def open_containing(url, possible_transports=None):
200
207
        return self.supports_tags() and self.tags.get_tag_dict()
201
208
 
202
209
    def get_config(self):
 
210
        """Get a bzrlib.config.BranchConfig for this Branch.
 
211
 
 
212
        This can then be used to get and set configuration options for the
 
213
        branch.
 
214
 
 
215
        :return: A bzrlib.config.BranchConfig.
 
216
        """
203
217
        return BranchConfig(self)
204
218
 
205
219
    def _get_config(self):
217
231
    def _get_fallback_repository(self, url):
218
232
        """Get the repository we fallback to at url."""
219
233
        url = urlutils.join(self.base, url)
220
 
        a_bzrdir = bzrdir.BzrDir.open(url,
 
234
        a_branch = Branch.open(url,
221
235
            possible_transports=[self.bzrdir.root_transport])
222
 
        return a_bzrdir.open_branch().repository
 
236
        return a_branch.repository
223
237
 
 
238
    @needs_read_lock
224
239
    def _get_tags_bytes(self):
225
240
        """Get the bytes of a serialised tags dict.
226
241
 
233
248
        :return: The bytes of the tags file.
234
249
        :seealso: Branch._set_tags_bytes.
235
250
        """
236
 
        return self._transport.get_bytes('tags')
 
251
        if self._tags_bytes is None:
 
252
            self._tags_bytes = self._transport.get_bytes('tags')
 
253
        return self._tags_bytes
237
254
 
238
255
    def _get_nick(self, local=False, possible_transports=None):
239
256
        config = self.get_config()
241
258
        if not local and not config.has_explicit_nickname():
242
259
            try:
243
260
                master = self.get_master_branch(possible_transports)
 
261
                if master and self.user_url == master.user_url:
 
262
                    raise errors.RecursiveBind(self.user_url)
244
263
                if master is not None:
245
264
                    # return the master branch value
246
265
                    return master.nick
 
266
            except errors.RecursiveBind, e:
 
267
                raise e
247
268
            except errors.BzrError, e:
248
269
                # Silently fall back to local implicit nick if the master is
249
270
                # unavailable
286
307
        new_history.reverse()
287
308
        return new_history
288
309
 
289
 
    def lock_write(self):
 
310
    def lock_write(self, token=None):
 
311
        """Lock the branch for write operations.
 
312
 
 
313
        :param token: A token to permit reacquiring a previously held and
 
314
            preserved lock.
 
315
        :return: A BranchWriteLockResult.
 
316
        """
290
317
        raise NotImplementedError(self.lock_write)
291
318
 
292
319
    def lock_read(self):
 
320
        """Lock the branch for read operations.
 
321
 
 
322
        :return: A bzrlib.lock.LogicalLockResult.
 
323
        """
293
324
        raise NotImplementedError(self.lock_read)
294
325
 
295
326
    def unlock(self):
420
451
            * 'include' - the stop revision is the last item in the result
421
452
            * 'with-merges' - include the stop revision and all of its
422
453
              merged revisions in the result
 
454
            * 'with-merges-without-common-ancestry' - filter out revisions 
 
455
              that are in both ancestries
423
456
        :param direction: either 'reverse' or 'forward':
424
457
            * reverse means return the start_revision_id first, i.e.
425
458
              start at the most recent revision and go backwards in history
447
480
        # start_revision_id.
448
481
        if self._merge_sorted_revisions_cache is None:
449
482
            last_revision = self.last_revision()
450
 
            last_key = (last_revision,)
451
 
            known_graph = self.repository.revisions.get_known_graph_ancestry(
452
 
                [last_key])
 
483
            known_graph = self.repository.get_known_graph_ancestry(
 
484
                [last_revision])
453
485
            self._merge_sorted_revisions_cache = known_graph.merge_sort(
454
 
                last_key)
 
486
                last_revision)
455
487
        filtered = self._filter_merge_sorted_revisions(
456
488
            self._merge_sorted_revisions_cache, start_revision_id,
457
489
            stop_revision_id, stop_rule)
 
490
        # Make sure we don't return revisions that are not part of the
 
491
        # start_revision_id ancestry.
 
492
        filtered = self._filter_start_non_ancestors(filtered)
458
493
        if direction == 'reverse':
459
494
            return filtered
460
495
        if direction == 'forward':
497
532
                       node.end_of_merge)
498
533
                if rev_id == stop_revision_id:
499
534
                    return
 
535
        elif stop_rule == 'with-merges-without-common-ancestry':
 
536
            # We want to exclude all revisions that are already part of the
 
537
            # stop_revision_id ancestry.
 
538
            graph = self.repository.get_graph()
 
539
            ancestors = graph.find_unique_ancestors(start_revision_id,
 
540
                                                    [stop_revision_id])
 
541
            for node in rev_iter:
 
542
                rev_id = node.key[-1]
 
543
                if rev_id not in ancestors:
 
544
                    continue
 
545
                yield (rev_id, node.merge_depth, node.revno,
 
546
                       node.end_of_merge)
500
547
        elif stop_rule == 'with-merges':
501
548
            stop_rev = self.repository.get_revision(stop_revision_id)
502
549
            if stop_rev.parent_ids:
525
572
        else:
526
573
            raise ValueError('invalid stop_rule %r' % stop_rule)
527
574
 
 
575
    def _filter_start_non_ancestors(self, rev_iter):
 
576
        # If we started from a dotted revno, we want to consider it as a tip
 
577
        # and don't want to yield revisions that are not part of its
 
578
        # ancestry. Given the order guaranteed by the merge sort, we will see
 
579
        # uninteresting descendants of the first parent of our tip before the
 
580
        # tip itself.
 
581
        first = rev_iter.next()
 
582
        (rev_id, merge_depth, revno, end_of_merge) = first
 
583
        yield first
 
584
        if not merge_depth:
 
585
            # We start at a mainline revision so by definition, all others
 
586
            # revisions in rev_iter are ancestors
 
587
            for node in rev_iter:
 
588
                yield node
 
589
 
 
590
        clean = False
 
591
        whitelist = set()
 
592
        pmap = self.repository.get_parent_map([rev_id])
 
593
        parents = pmap.get(rev_id, [])
 
594
        if parents:
 
595
            whitelist.update(parents)
 
596
        else:
 
597
            # If there is no parents, there is nothing of interest left
 
598
 
 
599
            # FIXME: It's hard to test this scenario here as this code is never
 
600
            # called in that case. -- vila 20100322
 
601
            return
 
602
 
 
603
        for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
 
604
            if not clean:
 
605
                if rev_id in whitelist:
 
606
                    pmap = self.repository.get_parent_map([rev_id])
 
607
                    parents = pmap.get(rev_id, [])
 
608
                    whitelist.remove(rev_id)
 
609
                    whitelist.update(parents)
 
610
                    if merge_depth == 0:
 
611
                        # We've reached the mainline, there is nothing left to
 
612
                        # filter
 
613
                        clean = True
 
614
                else:
 
615
                    # A revision that is not part of the ancestry of our
 
616
                    # starting revision.
 
617
                    continue
 
618
            yield (rev_id, merge_depth, revno, end_of_merge)
 
619
 
528
620
    def leave_lock_in_place(self):
529
621
        """Tell this branch object not to release the physical lock when this
530
622
        object is unlocked.
547
639
        :param other: The branch to bind to
548
640
        :type other: Branch
549
641
        """
550
 
        raise errors.UpgradeRequired(self.base)
 
642
        raise errors.UpgradeRequired(self.user_url)
551
643
 
552
644
    def set_append_revisions_only(self, enabled):
553
645
        if not self._format.supports_set_append_revisions_only():
554
 
            raise errors.UpgradeRequired(self.base)
 
646
            raise errors.UpgradeRequired(self.user_url)
555
647
        if enabled:
556
648
            value = 'True'
557
649
        else:
605
697
    def get_old_bound_location(self):
606
698
        """Return the URL of the branch we used to be bound to
607
699
        """
608
 
        raise errors.UpgradeRequired(self.base)
 
700
        raise errors.UpgradeRequired(self.user_url)
609
701
 
610
702
    def get_commit_builder(self, parents, config=None, timestamp=None,
611
703
                           timezone=None, committer=None, revprops=None,
689
781
            stacking.
690
782
        """
691
783
        if not self._format.supports_stacking():
692
 
            raise errors.UnstackableBranchFormat(self._format, self.base)
 
784
            raise errors.UnstackableBranchFormat(self._format, self.user_url)
693
785
        # XXX: Changing from one fallback repository to another does not check
694
786
        # that all the data you need is present in the new fallback.
695
787
        # Possibly it should.
724
816
            old_repository = self.repository
725
817
            if len(old_repository._fallback_repositories) != 1:
726
818
                raise AssertionError("can't cope with fallback repositories "
727
 
                    "of %r" % (self.repository,))
728
 
            # unlock it, including unlocking the fallback
 
819
                    "of %r (fallbacks: %r)" % (old_repository,
 
820
                        old_repository._fallback_repositories))
 
821
            # Open the new repository object.
 
822
            # Repositories don't offer an interface to remove fallback
 
823
            # repositories today; take the conceptually simpler option and just
 
824
            # reopen it.  We reopen it starting from the URL so that we
 
825
            # get a separate connection for RemoteRepositories and can
 
826
            # stream from one of them to the other.  This does mean doing
 
827
            # separate SSH connection setup, but unstacking is not a
 
828
            # common operation so it's tolerable.
 
829
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
 
830
            new_repository = new_bzrdir.find_repository()
 
831
            if new_repository._fallback_repositories:
 
832
                raise AssertionError("didn't expect %r to have "
 
833
                    "fallback_repositories"
 
834
                    % (self.repository,))
 
835
            # Replace self.repository with the new repository.
 
836
            # Do our best to transfer the lock state (i.e. lock-tokens and
 
837
            # lock count) of self.repository to the new repository.
 
838
            lock_token = old_repository.lock_write().repository_token
 
839
            self.repository = new_repository
 
840
            if isinstance(self, remote.RemoteBranch):
 
841
                # Remote branches can have a second reference to the old
 
842
                # repository that need to be replaced.
 
843
                if self._real_branch is not None:
 
844
                    self._real_branch.repository = new_repository
 
845
            self.repository.lock_write(token=lock_token)
 
846
            if lock_token is not None:
 
847
                old_repository.leave_lock_in_place()
729
848
            old_repository.unlock()
 
849
            if lock_token is not None:
 
850
                # XXX: self.repository.leave_lock_in_place() before this
 
851
                # function will not be preserved.  Fortunately that doesn't
 
852
                # affect the current default format (2a), and would be a
 
853
                # corner-case anyway.
 
854
                #  - Andrew Bennetts, 2010/06/30
 
855
                self.repository.dont_leave_lock_in_place()
 
856
            old_lock_count = 0
 
857
            while True:
 
858
                try:
 
859
                    old_repository.unlock()
 
860
                except errors.LockNotHeld:
 
861
                    break
 
862
                old_lock_count += 1
 
863
            if old_lock_count == 0:
 
864
                raise AssertionError(
 
865
                    'old_repository should have been locked at least once.')
 
866
            for i in range(old_lock_count-1):
 
867
                self.repository.lock_write()
 
868
            # Fetch from the old repository into the new.
730
869
            old_repository.lock_read()
731
870
            try:
732
 
                # Repositories don't offer an interface to remove fallback
733
 
                # repositories today; take the conceptually simpler option and just
734
 
                # reopen it.  We reopen it starting from the URL so that we
735
 
                # get a separate connection for RemoteRepositories and can
736
 
                # stream from one of them to the other.  This does mean doing
737
 
                # separate SSH connection setup, but unstacking is not a
738
 
                # common operation so it's tolerable.
739
 
                new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
740
 
                new_repository = new_bzrdir.find_repository()
741
 
                self.repository = new_repository
742
 
                if self.repository._fallback_repositories:
743
 
                    raise AssertionError("didn't expect %r to have "
744
 
                        "fallback_repositories"
745
 
                        % (self.repository,))
746
 
                # this is not paired with an unlock because it's just restoring
747
 
                # the previous state; the lock's released when set_stacked_on_url
748
 
                # returns
749
 
                self.repository.lock_write()
750
871
                # XXX: If you unstack a branch while it has a working tree
751
872
                # with a pending merge, the pending-merged revisions will no
752
873
                # longer be present.  You can (probably) revert and remerge.
767
888
 
768
889
        :seealso: Branch._get_tags_bytes.
769
890
        """
770
 
        return _run_with_write_locked_target(self, self._transport.put_bytes,
771
 
            'tags', bytes)
 
891
        return _run_with_write_locked_target(self, self._set_tags_bytes_locked,
 
892
                bytes)
 
893
 
 
894
    def _set_tags_bytes_locked(self, bytes):
 
895
        self._tags_bytes = bytes
 
896
        return self._transport.put_bytes('tags', bytes)
772
897
 
773
898
    def _cache_revision_history(self, rev_history):
774
899
        """Set the cached revision history to rev_history.
804
929
        self._merge_sorted_revisions_cache = None
805
930
        self._partial_revision_history_cache = []
806
931
        self._partial_revision_id_to_revno_cache = {}
 
932
        self._tags_bytes = None
807
933
 
808
934
    def _gen_revision_history(self):
809
935
        """Return sequence of revision hashes on to this branch.
846
972
 
847
973
    def unbind(self):
848
974
        """Older format branches cannot bind or unbind."""
849
 
        raise errors.UpgradeRequired(self.base)
 
975
        raise errors.UpgradeRequired(self.user_url)
850
976
 
851
977
    def last_revision(self):
852
978
        """Return last revision id, or NULL_REVISION."""
893
1019
                raise errors.NoSuchRevision(self, stop_revision)
894
1020
        return other_history[self_len:stop_revision]
895
1021
 
896
 
    @needs_write_lock
897
1022
    def update_revisions(self, other, stop_revision=None, overwrite=False,
898
1023
                         graph=None):
899
1024
        """Pull in new perfect-fit revisions.
948
1073
            self._extend_partial_history(distance_from_last)
949
1074
        return self._partial_revision_history_cache[distance_from_last]
950
1075
 
951
 
    @needs_write_lock
952
1076
    def pull(self, source, overwrite=False, stop_revision=None,
953
1077
             possible_transports=None, *args, **kwargs):
954
1078
        """Mirror source into this branch.
1012
1136
        try:
1013
1137
            return urlutils.join(self.base[:-1], parent)
1014
1138
        except errors.InvalidURLJoin, e:
1015
 
            raise errors.InaccessibleParent(parent, self.base)
 
1139
            raise errors.InaccessibleParent(parent, self.user_url)
1016
1140
 
1017
1141
    def _get_parent_location(self):
1018
1142
        raise NotImplementedError(self._get_parent_location)
1150
1274
        return result
1151
1275
 
1152
1276
    @needs_read_lock
1153
 
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
 
1277
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
 
1278
            repository=None):
1154
1279
        """Create a new line of development from the branch, into to_bzrdir.
1155
1280
 
1156
1281
        to_bzrdir controls the branch format.
1161
1286
        if (repository_policy is not None and
1162
1287
            repository_policy.requires_stacking()):
1163
1288
            to_bzrdir._format.require_stacking(_skip_repo=True)
1164
 
        result = to_bzrdir.create_branch()
 
1289
        result = to_bzrdir.create_branch(repository=repository)
1165
1290
        result.lock_write()
1166
1291
        try:
1167
1292
            if repository_policy is not None:
1197
1322
                revno = 1
1198
1323
        destination.set_last_revision_info(revno, revision_id)
1199
1324
 
1200
 
    @needs_read_lock
1201
1325
    def copy_content_into(self, destination, revision_id=None):
1202
1326
        """Copy the content of self into destination.
1203
1327
 
1204
1328
        revision_id: if not None, the revision history in the new branch will
1205
1329
                     be truncated to end with revision_id.
1206
1330
        """
1207
 
        self.update_references(destination)
1208
 
        self._synchronize_history(destination, revision_id)
1209
 
        try:
1210
 
            parent = self.get_parent()
1211
 
        except errors.InaccessibleParent, e:
1212
 
            mutter('parent was not accessible to copy: %s', e)
1213
 
        else:
1214
 
            if parent:
1215
 
                destination.set_parent(parent)
1216
 
        if self._push_should_merge_tags():
1217
 
            self.tags.merge_to(destination.tags)
 
1331
        return InterBranch.get(self, destination).copy_content_into(
 
1332
            revision_id=revision_id)
1218
1333
 
1219
1334
    def update_references(self, target):
1220
1335
        if not getattr(self._format, 'supports_reference_locations', False):
1275
1390
        return format
1276
1391
 
1277
1392
    def create_clone_on_transport(self, to_transport, revision_id=None,
1278
 
        stacked_on=None, create_prefix=False, use_existing_dir=False):
 
1393
        stacked_on=None, create_prefix=False, use_existing_dir=False,
 
1394
        no_tree=None):
1279
1395
        """Create a clone of this branch and its bzrdir.
1280
1396
 
1281
1397
        :param to_transport: The transport to clone onto.
1288
1404
        """
1289
1405
        # XXX: Fix the bzrdir API to allow getting the branch back from the
1290
1406
        # clone call. Or something. 20090224 RBC/spiv.
 
1407
        # XXX: Should this perhaps clone colocated branches as well, 
 
1408
        # rather than just the default branch? 20100319 JRV
1291
1409
        if revision_id is None:
1292
1410
            revision_id = self.last_revision()
1293
1411
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1294
1412
            revision_id=revision_id, stacked_on=stacked_on,
1295
 
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
 
1413
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
 
1414
            no_tree=no_tree)
1296
1415
        return dir_to.open_branch()
1297
1416
 
1298
1417
    def create_checkout(self, to_location, revision_id=None,
1317
1436
        if lightweight:
1318
1437
            format = self._get_checkout_format()
1319
1438
            checkout = format.initialize_on_transport(t)
1320
 
            from_branch = BranchReferenceFormat().initialize(checkout, self)
 
1439
            from_branch = BranchReferenceFormat().initialize(checkout, 
 
1440
                target_branch=self)
1321
1441
        else:
1322
1442
            format = self._get_checkout_format()
1323
1443
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1365
1485
    def supports_tags(self):
1366
1486
        return self._format.supports_tags()
1367
1487
 
 
1488
    def automatic_tag_name(self, revision_id):
 
1489
        """Try to automatically find the tag name for a revision.
 
1490
 
 
1491
        :param revision_id: Revision id of the revision.
 
1492
        :return: A tag name or None if no tag name could be determined.
 
1493
        """
 
1494
        for hook in Branch.hooks['automatic_tag_name']:
 
1495
            ret = hook(self, revision_id)
 
1496
            if ret is not None:
 
1497
                return ret
 
1498
        return None
 
1499
 
1368
1500
    def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1369
1501
                                         other_branch):
1370
1502
        """Ensure that revision_b is a descendant of revision_a.
1410
1542
     * an open routine.
1411
1543
 
1412
1544
    Formats are placed in an dict by their format string for reference
1413
 
    during branch opening. Its not required that these be instances, they
 
1545
    during branch opening. It's not required that these be instances, they
1414
1546
    can be classes themselves with class methods - it simply depends on
1415
1547
    whether state is needed for a given format or not.
1416
1548
 
1434
1566
        return not (self == other)
1435
1567
 
1436
1568
    @classmethod
1437
 
    def find_format(klass, a_bzrdir):
 
1569
    def find_format(klass, a_bzrdir, name=None):
1438
1570
        """Return the format for the branch object in a_bzrdir."""
1439
1571
        try:
1440
 
            transport = a_bzrdir.get_branch_transport(None)
 
1572
            transport = a_bzrdir.get_branch_transport(None, name=name)
1441
1573
            format_string = transport.get_bytes("format")
1442
 
            return klass._formats[format_string]
 
1574
            format = klass._formats[format_string]
 
1575
            if isinstance(format, MetaDirBranchFormatFactory):
 
1576
                return format()
 
1577
            return format
1443
1578
        except errors.NoSuchFile:
1444
1579
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1445
1580
        except KeyError:
1450
1585
        """Return the current default format."""
1451
1586
        return klass._default_format
1452
1587
 
1453
 
    def get_reference(self, a_bzrdir):
 
1588
    @classmethod
 
1589
    def get_formats(klass):
 
1590
        """Get all the known formats.
 
1591
 
 
1592
        Warning: This triggers a load of all lazy registered formats: do not
 
1593
        use except when that is desireed.
 
1594
        """
 
1595
        result = []
 
1596
        for fmt in klass._formats.values():
 
1597
            if isinstance(fmt, MetaDirBranchFormatFactory):
 
1598
                fmt = fmt()
 
1599
            result.append(fmt)
 
1600
        return result
 
1601
 
 
1602
    def get_reference(self, a_bzrdir, name=None):
1454
1603
        """Get the target reference of the branch in a_bzrdir.
1455
1604
 
1456
1605
        format probing must have been completed before calling
1458
1607
        in a_bzrdir is correct.
1459
1608
 
1460
1609
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1610
        :param name: Name of the colocated branch to fetch
1461
1611
        :return: None if the branch is not a reference branch.
1462
1612
        """
1463
1613
        return None
1464
1614
 
1465
1615
    @classmethod
1466
 
    def set_reference(self, a_bzrdir, to_branch):
 
1616
    def set_reference(self, a_bzrdir, name, to_branch):
1467
1617
        """Set the target reference of the branch in a_bzrdir.
1468
1618
 
1469
1619
        format probing must have been completed before calling
1471
1621
        in a_bzrdir is correct.
1472
1622
 
1473
1623
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1624
        :param name: Name of colocated branch to set, None for default
1474
1625
        :param to_branch: branch that the checkout is to reference
1475
1626
        """
1476
1627
        raise NotImplementedError(self.set_reference)
1483
1634
        """Return the short format description for this format."""
1484
1635
        raise NotImplementedError(self.get_format_description)
1485
1636
 
1486
 
    def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
 
1637
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
 
1638
        hooks = Branch.hooks['post_branch_init']
 
1639
        if not hooks:
 
1640
            return
 
1641
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
 
1642
        for hook in hooks:
 
1643
            hook(params)
 
1644
 
 
1645
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
1646
                           repository=None, lock_type='metadir',
1487
1647
                           set_format=True):
1488
1648
        """Initialize a branch in a bzrdir, with specified files
1489
1649
 
1490
1650
        :param a_bzrdir: The bzrdir to initialize the branch in
1491
1651
        :param utf8_files: The files to create as a list of
1492
1652
            (filename, content) tuples
 
1653
        :param name: Name of colocated branch to create, if any
1493
1654
        :param set_format: If True, set the format with
1494
1655
            self.get_format_string.  (BzrBranch4 has its format set
1495
1656
            elsewhere)
1496
1657
        :return: a branch in this format
1497
1658
        """
1498
 
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1499
 
        branch_transport = a_bzrdir.get_branch_transport(self)
 
1659
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
1660
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1500
1661
        lock_map = {
1501
1662
            'metadir': ('lock', lockdir.LockDir),
1502
1663
            'branch4': ('branch-lock', lockable_files.TransportLock),
1523
1684
        finally:
1524
1685
            if lock_taken:
1525
1686
                control_files.unlock()
1526
 
        return self.open(a_bzrdir, _found=True)
 
1687
        branch = self.open(a_bzrdir, name, _found=True,
 
1688
                found_repository=repository)
 
1689
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
1690
        return branch
1527
1691
 
1528
 
    def initialize(self, a_bzrdir):
1529
 
        """Create a branch of this format in a_bzrdir."""
 
1692
    def initialize(self, a_bzrdir, name=None, repository=None):
 
1693
        """Create a branch of this format in a_bzrdir.
 
1694
        
 
1695
        :param name: Name of the colocated branch to create.
 
1696
        """
1530
1697
        raise NotImplementedError(self.initialize)
1531
1698
 
1532
1699
    def is_supported(self):
1562
1729
        """
1563
1730
        raise NotImplementedError(self.network_name)
1564
1731
 
1565
 
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
 
1732
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
 
1733
            found_repository=None):
1566
1734
        """Return the branch object for a_bzrdir
1567
1735
 
1568
1736
        :param a_bzrdir: A BzrDir that contains a branch.
 
1737
        :param name: Name of colocated branch to open
1569
1738
        :param _found: a private parameter, do not use it. It is used to
1570
1739
            indicate if format probing has already be done.
1571
1740
        :param ignore_fallbacks: when set, no fallback branches will be opened
1575
1744
 
1576
1745
    @classmethod
1577
1746
    def register_format(klass, format):
1578
 
        """Register a metadir format."""
 
1747
        """Register a metadir format.
 
1748
        
 
1749
        See MetaDirBranchFormatFactory for the ability to register a format
 
1750
        without loading the code the format needs until it is actually used.
 
1751
        """
1579
1752
        klass._formats[format.get_format_string()] = format
1580
1753
        # Metadir formats have a network name of their format string, and get
1581
 
        # registered as class factories.
1582
 
        network_format_registry.register(format.get_format_string(), format.__class__)
 
1754
        # registered as factories.
 
1755
        if isinstance(format, MetaDirBranchFormatFactory):
 
1756
            network_format_registry.register(format.get_format_string(), format)
 
1757
        else:
 
1758
            network_format_registry.register(format.get_format_string(),
 
1759
                format.__class__)
1583
1760
 
1584
1761
    @classmethod
1585
1762
    def set_default_format(klass, format):
1605
1782
        return False  # by default
1606
1783
 
1607
1784
 
 
1785
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
 
1786
    """A factory for a BranchFormat object, permitting simple lazy registration.
 
1787
    
 
1788
    While none of the built in BranchFormats are lazy registered yet,
 
1789
    bzrlib.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
 
1790
    use it, and the bzr-loom plugin uses it as well (see
 
1791
    bzrlib.plugins.loom.formats).
 
1792
    """
 
1793
 
 
1794
    def __init__(self, format_string, module_name, member_name):
 
1795
        """Create a MetaDirBranchFormatFactory.
 
1796
 
 
1797
        :param format_string: The format string the format has.
 
1798
        :param module_name: Module to load the format class from.
 
1799
        :param member_name: Attribute name within the module for the format class.
 
1800
        """
 
1801
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
 
1802
        self._format_string = format_string
 
1803
        
 
1804
    def get_format_string(self):
 
1805
        """See BranchFormat.get_format_string."""
 
1806
        return self._format_string
 
1807
 
 
1808
    def __call__(self):
 
1809
        """Used for network_format_registry support."""
 
1810
        return self.get_obj()()
 
1811
 
 
1812
 
1608
1813
class BranchHooks(Hooks):
1609
1814
    """A dictionary mapping hook name to a list of callables for branch hooks.
1610
1815
 
1637
1842
            "with a bzrlib.branch.PullResult object and only runs in the "
1638
1843
            "bzr client.", (0, 15), None))
1639
1844
        self.create_hook(HookPoint('pre_commit',
1640
 
            "Called after a commit is calculated but before it is is "
 
1845
            "Called after a commit is calculated but before it is "
1641
1846
            "completed. pre_commit is called with (local, master, old_revno, "
1642
1847
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1643
1848
            "). old_revid is NULL_REVISION for the first commit to a branch, "
1679
1884
            "multiple hooks installed for transform_fallback_location, "
1680
1885
            "all are called with the url returned from the previous hook."
1681
1886
            "The order is however undefined.", (1, 9), None))
 
1887
        self.create_hook(HookPoint('automatic_tag_name',
 
1888
            "Called to determine an automatic tag name for a revision. "
 
1889
            "automatic_tag_name is called with (branch, revision_id) and "
 
1890
            "should return a tag name or None if no tag name could be "
 
1891
            "determined. The first non-None tag name returned will be used.",
 
1892
            (2, 2), None))
 
1893
        self.create_hook(HookPoint('post_branch_init',
 
1894
            "Called after new branch initialization completes. "
 
1895
            "post_branch_init is called with a "
 
1896
            "bzrlib.branch.BranchInitHookParams. "
 
1897
            "Note that init, branch and checkout (both heavyweight and "
 
1898
            "lightweight) will all trigger this hook.", (2, 2), None))
 
1899
        self.create_hook(HookPoint('post_switch',
 
1900
            "Called after a checkout switches branch. "
 
1901
            "post_switch is called with a "
 
1902
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
 
1903
 
1682
1904
 
1683
1905
 
1684
1906
# install the default hooks into the Branch class.
1723
1945
            self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1724
1946
 
1725
1947
 
 
1948
class BranchInitHookParams(object):
 
1949
    """Object holding parameters passed to *_branch_init hooks.
 
1950
 
 
1951
    There are 4 fields that hooks may wish to access:
 
1952
 
 
1953
    :ivar format: the branch format
 
1954
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
 
1955
    :ivar name: name of colocated branch, if any (or None)
 
1956
    :ivar branch: the branch created
 
1957
 
 
1958
    Note that for lightweight checkouts, the bzrdir and format fields refer to
 
1959
    the checkout, hence they are different from the corresponding fields in
 
1960
    branch, which refer to the original branch.
 
1961
    """
 
1962
 
 
1963
    def __init__(self, format, a_bzrdir, name, branch):
 
1964
        """Create a group of BranchInitHook parameters.
 
1965
 
 
1966
        :param format: the branch format
 
1967
        :param a_bzrdir: the BzrDir where the branch will be/has been
 
1968
            initialized
 
1969
        :param name: name of colocated branch, if any (or None)
 
1970
        :param branch: the branch created
 
1971
 
 
1972
        Note that for lightweight checkouts, the bzrdir and format fields refer
 
1973
        to the checkout, hence they are different from the corresponding fields
 
1974
        in branch, which refer to the original branch.
 
1975
        """
 
1976
        self.format = format
 
1977
        self.bzrdir = a_bzrdir
 
1978
        self.name = name
 
1979
        self.branch = branch
 
1980
 
 
1981
    def __eq__(self, other):
 
1982
        return self.__dict__ == other.__dict__
 
1983
 
 
1984
    def __repr__(self):
 
1985
        return "<%s of %s>" % (self.__class__.__name__, self.branch)
 
1986
 
 
1987
 
 
1988
class SwitchHookParams(object):
 
1989
    """Object holding parameters passed to *_switch hooks.
 
1990
 
 
1991
    There are 4 fields that hooks may wish to access:
 
1992
 
 
1993
    :ivar control_dir: BzrDir of the checkout to change
 
1994
    :ivar to_branch: branch that the checkout is to reference
 
1995
    :ivar force: skip the check for local commits in a heavy checkout
 
1996
    :ivar revision_id: revision ID to switch to (or None)
 
1997
    """
 
1998
 
 
1999
    def __init__(self, control_dir, to_branch, force, revision_id):
 
2000
        """Create a group of SwitchHook parameters.
 
2001
 
 
2002
        :param control_dir: BzrDir of the checkout to change
 
2003
        :param to_branch: branch that the checkout is to reference
 
2004
        :param force: skip the check for local commits in a heavy checkout
 
2005
        :param revision_id: revision ID to switch to (or None)
 
2006
        """
 
2007
        self.control_dir = control_dir
 
2008
        self.to_branch = to_branch
 
2009
        self.force = force
 
2010
        self.revision_id = revision_id
 
2011
 
 
2012
    def __eq__(self, other):
 
2013
        return self.__dict__ == other.__dict__
 
2014
 
 
2015
    def __repr__(self):
 
2016
        return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
 
2017
            self.control_dir, self.to_branch,
 
2018
            self.revision_id)
 
2019
 
 
2020
 
1726
2021
class BzrBranchFormat4(BranchFormat):
1727
2022
    """Bzr branch format 4.
1728
2023
 
1735
2030
        """See BranchFormat.get_format_description()."""
1736
2031
        return "Branch format 4"
1737
2032
 
1738
 
    def initialize(self, a_bzrdir):
 
2033
    def initialize(self, a_bzrdir, name=None, repository=None):
1739
2034
        """Create a branch of this format in a_bzrdir."""
 
2035
        if repository is not None:
 
2036
            raise NotImplementedError(
 
2037
                "initialize(repository=<not None>) on %r" % (self,))
1740
2038
        utf8_files = [('revision-history', ''),
1741
2039
                      ('branch-name', ''),
1742
2040
                      ]
1743
 
        return self._initialize_helper(a_bzrdir, utf8_files,
 
2041
        return self._initialize_helper(a_bzrdir, utf8_files, name=name,
1744
2042
                                       lock_type='branch4', set_format=False)
1745
2043
 
1746
2044
    def __init__(self):
1751
2049
        """The network name for this format is the control dirs disk label."""
1752
2050
        return self._matchingbzrdir.get_format_string()
1753
2051
 
1754
 
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
 
2052
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
 
2053
            found_repository=None):
1755
2054
        """See BranchFormat.open()."""
1756
2055
        if not _found:
1757
2056
            # we are being called directly and must probe.
1758
2057
            raise NotImplementedError
 
2058
        if found_repository is None:
 
2059
            found_repository = a_bzrdir.open_repository()
1759
2060
        return BzrBranch(_format=self,
1760
2061
                         _control_files=a_bzrdir._control_files,
1761
2062
                         a_bzrdir=a_bzrdir,
1762
 
                         _repository=a_bzrdir.open_repository())
 
2063
                         name=name,
 
2064
                         _repository=found_repository)
1763
2065
 
1764
2066
    def __str__(self):
1765
2067
        return "Bazaar-NG branch format 4"
1779
2081
        """
1780
2082
        return self.get_format_string()
1781
2083
 
1782
 
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
 
2084
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
 
2085
            found_repository=None):
1783
2086
        """See BranchFormat.open()."""
1784
2087
        if not _found:
1785
 
            format = BranchFormat.find_format(a_bzrdir)
 
2088
            format = BranchFormat.find_format(a_bzrdir, name=name)
1786
2089
            if format.__class__ != self.__class__:
1787
2090
                raise AssertionError("wrong format %r found for %r" %
1788
2091
                    (format, self))
 
2092
        transport = a_bzrdir.get_branch_transport(None, name=name)
1789
2093
        try:
1790
 
            transport = a_bzrdir.get_branch_transport(None)
1791
2094
            control_files = lockable_files.LockableFiles(transport, 'lock',
1792
2095
                                                         lockdir.LockDir)
 
2096
            if found_repository is None:
 
2097
                found_repository = a_bzrdir.find_repository()
1793
2098
            return self._branch_class()(_format=self,
1794
2099
                              _control_files=control_files,
 
2100
                              name=name,
1795
2101
                              a_bzrdir=a_bzrdir,
1796
 
                              _repository=a_bzrdir.find_repository(),
 
2102
                              _repository=found_repository,
1797
2103
                              ignore_fallbacks=ignore_fallbacks)
1798
2104
        except errors.NoSuchFile:
1799
2105
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1831
2137
        """See BranchFormat.get_format_description()."""
1832
2138
        return "Branch format 5"
1833
2139
 
1834
 
    def initialize(self, a_bzrdir):
 
2140
    def initialize(self, a_bzrdir, name=None, repository=None):
1835
2141
        """Create a branch of this format in a_bzrdir."""
1836
2142
        utf8_files = [('revision-history', ''),
1837
2143
                      ('branch-name', ''),
1838
2144
                      ]
1839
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
2145
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
1840
2146
 
1841
2147
    def supports_tags(self):
1842
2148
        return False
1864
2170
        """See BranchFormat.get_format_description()."""
1865
2171
        return "Branch format 6"
1866
2172
 
1867
 
    def initialize(self, a_bzrdir):
 
2173
    def initialize(self, a_bzrdir, name=None, repository=None):
1868
2174
        """Create a branch of this format in a_bzrdir."""
1869
2175
        utf8_files = [('last-revision', '0 null:\n'),
1870
2176
                      ('branch.conf', ''),
1871
2177
                      ('tags', ''),
1872
2178
                      ]
1873
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
2179
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
1874
2180
 
1875
2181
    def make_tags(self, branch):
1876
2182
        """See bzrlib.branch.BranchFormat.make_tags()."""
1894
2200
        """See BranchFormat.get_format_description()."""
1895
2201
        return "Branch format 8"
1896
2202
 
1897
 
    def initialize(self, a_bzrdir):
 
2203
    def initialize(self, a_bzrdir, name=None, repository=None):
1898
2204
        """Create a branch of this format in a_bzrdir."""
1899
2205
        utf8_files = [('last-revision', '0 null:\n'),
1900
2206
                      ('branch.conf', ''),
1901
2207
                      ('tags', ''),
1902
2208
                      ('references', '')
1903
2209
                      ]
1904
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
2210
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
1905
2211
 
1906
2212
    def __init__(self):
1907
2213
        super(BzrBranchFormat8, self).__init__()
1930
2236
    This format was introduced in bzr 1.6.
1931
2237
    """
1932
2238
 
1933
 
    def initialize(self, a_bzrdir):
 
2239
    def initialize(self, a_bzrdir, name=None, repository=None):
1934
2240
        """Create a branch of this format in a_bzrdir."""
1935
2241
        utf8_files = [('last-revision', '0 null:\n'),
1936
2242
                      ('branch.conf', ''),
1937
2243
                      ('tags', ''),
1938
2244
                      ]
1939
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
2245
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
1940
2246
 
1941
2247
    def _branch_class(self):
1942
2248
        return BzrBranch7
1974
2280
        """See BranchFormat.get_format_description()."""
1975
2281
        return "Checkout reference format 1"
1976
2282
 
1977
 
    def get_reference(self, a_bzrdir):
 
2283
    def get_reference(self, a_bzrdir, name=None):
1978
2284
        """See BranchFormat.get_reference()."""
1979
 
        transport = a_bzrdir.get_branch_transport(None)
 
2285
        transport = a_bzrdir.get_branch_transport(None, name=name)
1980
2286
        return transport.get_bytes('location')
1981
2287
 
1982
 
    def set_reference(self, a_bzrdir, to_branch):
 
2288
    def set_reference(self, a_bzrdir, name, to_branch):
1983
2289
        """See BranchFormat.set_reference()."""
1984
 
        transport = a_bzrdir.get_branch_transport(None)
 
2290
        transport = a_bzrdir.get_branch_transport(None, name=name)
1985
2291
        location = transport.put_bytes('location', to_branch.base)
1986
2292
 
1987
 
    def initialize(self, a_bzrdir, target_branch=None):
 
2293
    def initialize(self, a_bzrdir, name=None, target_branch=None,
 
2294
            repository=None):
1988
2295
        """Create a branch of this format in a_bzrdir."""
1989
2296
        if target_branch is None:
1990
2297
            # this format does not implement branch itself, thus the implicit
1991
2298
            # creation contract must see it as uninitializable
1992
2299
            raise errors.UninitializableFormat(self)
1993
 
        mutter('creating branch reference in %s', a_bzrdir.transport.base)
1994
 
        branch_transport = a_bzrdir.get_branch_transport(self)
 
2300
        mutter('creating branch reference in %s', a_bzrdir.user_url)
 
2301
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1995
2302
        branch_transport.put_bytes('location',
1996
 
            target_branch.bzrdir.root_transport.base)
 
2303
            target_branch.bzrdir.user_url)
1997
2304
        branch_transport.put_bytes('format', self.get_format_string())
1998
 
        return self.open(
1999
 
            a_bzrdir, _found=True,
 
2305
        branch = self.open(
 
2306
            a_bzrdir, name, _found=True,
2000
2307
            possible_transports=[target_branch.bzrdir.root_transport])
 
2308
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
2309
        return branch
2001
2310
 
2002
2311
    def __init__(self):
2003
2312
        super(BranchReferenceFormat, self).__init__()
2009
2318
        def clone(to_bzrdir, revision_id=None,
2010
2319
            repository_policy=None):
2011
2320
            """See Branch.clone()."""
2012
 
            return format.initialize(to_bzrdir, a_branch)
 
2321
            return format.initialize(to_bzrdir, target_branch=a_branch)
2013
2322
            # cannot obey revision_id limits when cloning a reference ...
2014
2323
            # FIXME RBC 20060210 either nuke revision_id for clone, or
2015
2324
            # emit some sort of warning/error to the caller ?!
2016
2325
        return clone
2017
2326
 
2018
 
    def open(self, a_bzrdir, _found=False, location=None,
2019
 
             possible_transports=None, ignore_fallbacks=False):
 
2327
    def open(self, a_bzrdir, name=None, _found=False, location=None,
 
2328
             possible_transports=None, ignore_fallbacks=False,
 
2329
             found_repository=None):
2020
2330
        """Return the branch that the branch reference in a_bzrdir points at.
2021
2331
 
2022
2332
        :param a_bzrdir: A BzrDir that contains a branch.
 
2333
        :param name: Name of colocated branch to open, if any
2023
2334
        :param _found: a private parameter, do not use it. It is used to
2024
2335
            indicate if format probing has already be done.
2025
2336
        :param ignore_fallbacks: when set, no fallback branches will be opened
2030
2341
        :param possible_transports: An optional reusable transports list.
2031
2342
        """
2032
2343
        if not _found:
2033
 
            format = BranchFormat.find_format(a_bzrdir)
 
2344
            format = BranchFormat.find_format(a_bzrdir, name=name)
2034
2345
            if format.__class__ != self.__class__:
2035
2346
                raise AssertionError("wrong format %r found for %r" %
2036
2347
                    (format, self))
2037
2348
        if location is None:
2038
 
            location = self.get_reference(a_bzrdir)
 
2349
            location = self.get_reference(a_bzrdir, name)
2039
2350
        real_bzrdir = bzrdir.BzrDir.open(
2040
2351
            location, possible_transports=possible_transports)
2041
 
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
 
2352
        result = real_bzrdir.open_branch(name=name, 
 
2353
            ignore_fallbacks=ignore_fallbacks)
2042
2354
        # this changes the behaviour of result.clone to create a new reference
2043
2355
        # rather than a copy of the content of the branch.
2044
2356
        # I did not use a proxy object because that needs much more extensive
2078
2390
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2079
2391
 
2080
2392
 
 
2393
class BranchWriteLockResult(LogicalLockResult):
 
2394
    """The result of write locking a branch.
 
2395
 
 
2396
    :ivar branch_token: The token obtained from the underlying branch lock, or
 
2397
        None.
 
2398
    :ivar unlock: A callable which will unlock the lock.
 
2399
    """
 
2400
 
 
2401
    def __init__(self, unlock, branch_token):
 
2402
        LogicalLockResult.__init__(self, unlock)
 
2403
        self.branch_token = branch_token
 
2404
 
 
2405
    def __repr__(self):
 
2406
        return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
 
2407
            self.unlock)
 
2408
 
 
2409
 
2081
2410
class BzrBranch(Branch, _RelockDebugMixin):
2082
2411
    """A branch stored in the actual filesystem.
2083
2412
 
2090
2419
    :ivar repository: Repository for this branch.
2091
2420
    :ivar base: The url of the base directory for this branch; the one
2092
2421
        containing the .bzr directory.
 
2422
    :ivar name: Optional colocated branch name as it exists in the control
 
2423
        directory.
2093
2424
    """
2094
2425
 
2095
2426
    def __init__(self, _format=None,
2096
 
                 _control_files=None, a_bzrdir=None, _repository=None,
2097
 
                 ignore_fallbacks=False):
 
2427
                 _control_files=None, a_bzrdir=None, name=None,
 
2428
                 _repository=None, ignore_fallbacks=False):
2098
2429
        """Create new branch object at a particular location."""
2099
2430
        if a_bzrdir is None:
2100
2431
            raise ValueError('a_bzrdir must be supplied')
2101
2432
        else:
2102
2433
            self.bzrdir = a_bzrdir
2103
2434
        self._base = self.bzrdir.transport.clone('..').base
 
2435
        self.name = name
2104
2436
        # XXX: We should be able to just do
2105
2437
        #   self.base = self.bzrdir.root_transport.base
2106
2438
        # but this does not quite work yet -- mbp 20080522
2113
2445
        Branch.__init__(self)
2114
2446
 
2115
2447
    def __str__(self):
2116
 
        return '%s(%r)' % (self.__class__.__name__, self.base)
 
2448
        if self.name is None:
 
2449
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
 
2450
        else:
 
2451
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
 
2452
                self.name)
2117
2453
 
2118
2454
    __repr__ = __str__
2119
2455
 
2130
2466
        return self.control_files.is_locked()
2131
2467
 
2132
2468
    def lock_write(self, token=None):
 
2469
        """Lock the branch for write operations.
 
2470
 
 
2471
        :param token: A token to permit reacquiring a previously held and
 
2472
            preserved lock.
 
2473
        :return: A BranchWriteLockResult.
 
2474
        """
2133
2475
        if not self.is_locked():
2134
2476
            self._note_lock('w')
2135
2477
        # All-in-one needs to always unlock/lock.
2141
2483
        else:
2142
2484
            took_lock = False
2143
2485
        try:
2144
 
            return self.control_files.lock_write(token=token)
 
2486
            return BranchWriteLockResult(self.unlock,
 
2487
                self.control_files.lock_write(token=token))
2145
2488
        except:
2146
2489
            if took_lock:
2147
2490
                self.repository.unlock()
2148
2491
            raise
2149
2492
 
2150
2493
    def lock_read(self):
 
2494
        """Lock the branch for read operations.
 
2495
 
 
2496
        :return: A bzrlib.lock.LogicalLockResult.
 
2497
        """
2151
2498
        if not self.is_locked():
2152
2499
            self._note_lock('r')
2153
2500
        # All-in-one needs to always unlock/lock.
2160
2507
            took_lock = False
2161
2508
        try:
2162
2509
            self.control_files.lock_read()
 
2510
            return LogicalLockResult(self.unlock)
2163
2511
        except:
2164
2512
            if took_lock:
2165
2513
                self.repository.unlock()
2334
2682
        return result
2335
2683
 
2336
2684
    def get_stacked_on_url(self):
2337
 
        raise errors.UnstackableBranchFormat(self._format, self.base)
 
2685
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2338
2686
 
2339
2687
    def set_push_location(self, location):
2340
2688
        """See Branch.set_push_location."""
2530
2878
        if _mod_revision.is_null(last_revision):
2531
2879
            return
2532
2880
        if last_revision not in self._lefthand_history(revision_id):
2533
 
            raise errors.AppendRevisionsOnlyViolation(self.base)
 
2881
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
2534
2882
 
2535
2883
    def _gen_revision_history(self):
2536
2884
        """Generate the revision history from last revision
2636
2984
        if branch_location is None:
2637
2985
            return Branch.reference_parent(self, file_id, path,
2638
2986
                                           possible_transports)
2639
 
        branch_location = urlutils.join(self.base, branch_location)
 
2987
        branch_location = urlutils.join(self.user_url, branch_location)
2640
2988
        return Branch.open(branch_location,
2641
2989
                           possible_transports=possible_transports)
2642
2990
 
2688
3036
        return stacked_url
2689
3037
 
2690
3038
    def _get_append_revisions_only(self):
2691
 
        value = self.get_config().get_user_option('append_revisions_only')
2692
 
        return value == 'True'
 
3039
        return self.get_config(
 
3040
            ).get_user_option_as_bool('append_revisions_only')
2693
3041
 
2694
3042
    @needs_write_lock
2695
3043
    def generate_revision_history(self, revision_id, last_rev=None,
2757
3105
    """
2758
3106
 
2759
3107
    def get_stacked_on_url(self):
2760
 
        raise errors.UnstackableBranchFormat(self._format, self.base)
 
3108
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2761
3109
 
2762
3110
 
2763
3111
######################################################################
2789
3137
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
2790
3138
    """
2791
3139
 
 
3140
    @deprecated_method(deprecated_in((2, 3, 0)))
2792
3141
    def __int__(self):
2793
 
        # DEPRECATED: pull used to return the change in revno
 
3142
        """Return the relative change in revno.
 
3143
 
 
3144
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3145
        """
2794
3146
        return self.new_revno - self.old_revno
2795
3147
 
2796
3148
    def report(self, to_file):
2821
3173
        target, otherwise it will be None.
2822
3174
    """
2823
3175
 
 
3176
    @deprecated_method(deprecated_in((2, 3, 0)))
2824
3177
    def __int__(self):
2825
 
        # DEPRECATED: push used to return the change in revno
 
3178
        """Return the relative change in revno.
 
3179
 
 
3180
        :deprecated: Use `new_revno` and `old_revno` instead.
 
3181
        """
2826
3182
        return self.new_revno - self.old_revno
2827
3183
 
2828
3184
    def report(self, to_file):
2850
3206
        :param verbose: Requests more detailed display of what was checked,
2851
3207
            if any.
2852
3208
        """
2853
 
        note('checked branch %s format %s', self.branch.base,
 
3209
        note('checked branch %s format %s', self.branch.user_url,
2854
3210
            self.branch._format)
2855
3211
        for error in self.errors:
2856
3212
            note('found error:%s', error)
2951
3307
    _optimisers = []
2952
3308
    """The available optimised InterBranch types."""
2953
3309
 
2954
 
    @staticmethod
2955
 
    def _get_branch_formats_to_test():
2956
 
        """Return a tuple with the Branch formats to use when testing."""
2957
 
        raise NotImplementedError(InterBranch._get_branch_formats_to_test)
 
3310
    @classmethod
 
3311
    def _get_branch_formats_to_test(klass):
 
3312
        """Return an iterable of format tuples for testing.
 
3313
        
 
3314
        :return: An iterable of (from_format, to_format) to use when testing
 
3315
            this InterBranch class. Each InterBranch class should define this
 
3316
            method itself.
 
3317
        """
 
3318
        raise NotImplementedError(klass._get_branch_formats_to_test)
2958
3319
 
 
3320
    @needs_write_lock
2959
3321
    def pull(self, overwrite=False, stop_revision=None,
2960
3322
             possible_transports=None, local=False):
2961
3323
        """Mirror source into target branch.
2966
3328
        """
2967
3329
        raise NotImplementedError(self.pull)
2968
3330
 
 
3331
    @needs_write_lock
2969
3332
    def update_revisions(self, stop_revision=None, overwrite=False,
2970
3333
                         graph=None):
2971
3334
        """Pull in new perfect-fit revisions.
2979
3342
        """
2980
3343
        raise NotImplementedError(self.update_revisions)
2981
3344
 
 
3345
    @needs_write_lock
2982
3346
    def push(self, overwrite=False, stop_revision=None,
2983
3347
             _override_hook_source_branch=None):
2984
3348
        """Mirror the source branch into the target branch.
2987
3351
        """
2988
3352
        raise NotImplementedError(self.push)
2989
3353
 
 
3354
    @needs_write_lock
 
3355
    def copy_content_into(self, revision_id=None):
 
3356
        """Copy the content of source into target
 
3357
 
 
3358
        revision_id: if not None, the revision history in the new branch will
 
3359
                     be truncated to end with revision_id.
 
3360
        """
 
3361
        raise NotImplementedError(self.copy_content_into)
 
3362
 
2990
3363
 
2991
3364
class GenericInterBranch(InterBranch):
2992
 
    """InterBranch implementation that uses public Branch functions.
2993
 
    """
2994
 
 
2995
 
    @staticmethod
2996
 
    def _get_branch_formats_to_test():
2997
 
        return BranchFormat._default_format, BranchFormat._default_format
2998
 
 
 
3365
    """InterBranch implementation that uses public Branch functions."""
 
3366
 
 
3367
    @classmethod
 
3368
    def is_compatible(klass, source, target):
 
3369
        # GenericBranch uses the public API, so always compatible
 
3370
        return True
 
3371
 
 
3372
    @classmethod
 
3373
    def _get_branch_formats_to_test(klass):
 
3374
        return [(BranchFormat._default_format, BranchFormat._default_format)]
 
3375
 
 
3376
    @classmethod
 
3377
    def unwrap_format(klass, format):
 
3378
        if isinstance(format, remote.RemoteBranchFormat):
 
3379
            format._ensure_real()
 
3380
            return format._custom_format
 
3381
        return format
 
3382
 
 
3383
    @needs_write_lock
 
3384
    def copy_content_into(self, revision_id=None):
 
3385
        """Copy the content of source into target
 
3386
 
 
3387
        revision_id: if not None, the revision history in the new branch will
 
3388
                     be truncated to end with revision_id.
 
3389
        """
 
3390
        self.source.update_references(self.target)
 
3391
        self.source._synchronize_history(self.target, revision_id)
 
3392
        try:
 
3393
            parent = self.source.get_parent()
 
3394
        except errors.InaccessibleParent, e:
 
3395
            mutter('parent was not accessible to copy: %s', e)
 
3396
        else:
 
3397
            if parent:
 
3398
                self.target.set_parent(parent)
 
3399
        if self.source._push_should_merge_tags():
 
3400
            self.source.tags.merge_to(self.target.tags)
 
3401
 
 
3402
    @needs_write_lock
2999
3403
    def update_revisions(self, stop_revision=None, overwrite=False,
3000
3404
        graph=None):
3001
3405
        """See InterBranch.update_revisions()."""
3002
 
        self.source.lock_read()
3003
 
        try:
3004
 
            other_revno, other_last_revision = self.source.last_revision_info()
3005
 
            stop_revno = None # unknown
3006
 
            if stop_revision is None:
3007
 
                stop_revision = other_last_revision
3008
 
                if _mod_revision.is_null(stop_revision):
3009
 
                    # if there are no commits, we're done.
3010
 
                    return
3011
 
                stop_revno = other_revno
3012
 
 
3013
 
            # what's the current last revision, before we fetch [and change it
3014
 
            # possibly]
3015
 
            last_rev = _mod_revision.ensure_null(self.target.last_revision())
3016
 
            # we fetch here so that we don't process data twice in the common
3017
 
            # case of having something to pull, and so that the check for
3018
 
            # already merged can operate on the just fetched graph, which will
3019
 
            # be cached in memory.
3020
 
            self.target.fetch(self.source, stop_revision)
3021
 
            # Check to see if one is an ancestor of the other
3022
 
            if not overwrite:
3023
 
                if graph is None:
3024
 
                    graph = self.target.repository.get_graph()
3025
 
                if self.target._check_if_descendant_or_diverged(
3026
 
                        stop_revision, last_rev, graph, self.source):
3027
 
                    # stop_revision is a descendant of last_rev, but we aren't
3028
 
                    # overwriting, so we're done.
3029
 
                    return
3030
 
            if stop_revno is None:
3031
 
                if graph is None:
3032
 
                    graph = self.target.repository.get_graph()
3033
 
                this_revno, this_last_revision = \
3034
 
                        self.target.last_revision_info()
3035
 
                stop_revno = graph.find_distance_to_null(stop_revision,
3036
 
                                [(other_last_revision, other_revno),
3037
 
                                 (this_last_revision, this_revno)])
3038
 
            self.target.set_last_revision_info(stop_revno, stop_revision)
3039
 
        finally:
3040
 
            self.source.unlock()
3041
 
 
 
3406
        other_revno, other_last_revision = self.source.last_revision_info()
 
3407
        stop_revno = None # unknown
 
3408
        if stop_revision is None:
 
3409
            stop_revision = other_last_revision
 
3410
            if _mod_revision.is_null(stop_revision):
 
3411
                # if there are no commits, we're done.
 
3412
                return
 
3413
            stop_revno = other_revno
 
3414
 
 
3415
        # what's the current last revision, before we fetch [and change it
 
3416
        # possibly]
 
3417
        last_rev = _mod_revision.ensure_null(self.target.last_revision())
 
3418
        # we fetch here so that we don't process data twice in the common
 
3419
        # case of having something to pull, and so that the check for
 
3420
        # already merged can operate on the just fetched graph, which will
 
3421
        # be cached in memory.
 
3422
        self.target.fetch(self.source, stop_revision)
 
3423
        # Check to see if one is an ancestor of the other
 
3424
        if not overwrite:
 
3425
            if graph is None:
 
3426
                graph = self.target.repository.get_graph()
 
3427
            if self.target._check_if_descendant_or_diverged(
 
3428
                    stop_revision, last_rev, graph, self.source):
 
3429
                # stop_revision is a descendant of last_rev, but we aren't
 
3430
                # overwriting, so we're done.
 
3431
                return
 
3432
        if stop_revno is None:
 
3433
            if graph is None:
 
3434
                graph = self.target.repository.get_graph()
 
3435
            this_revno, this_last_revision = \
 
3436
                    self.target.last_revision_info()
 
3437
            stop_revno = graph.find_distance_to_null(stop_revision,
 
3438
                            [(other_last_revision, other_revno),
 
3439
                             (this_last_revision, this_revno)])
 
3440
        self.target.set_last_revision_info(stop_revno, stop_revision)
 
3441
 
 
3442
    @needs_write_lock
3042
3443
    def pull(self, overwrite=False, stop_revision=None,
3043
 
             possible_transports=None, _hook_master=None, run_hooks=True,
 
3444
             possible_transports=None, run_hooks=True,
3044
3445
             _override_hook_target=None, local=False):
3045
 
        """See Branch.pull.
 
3446
        """Pull from source into self, updating my master if any.
3046
3447
 
3047
 
        :param _hook_master: Private parameter - set the branch to
3048
 
            be supplied as the master to pull hooks.
3049
3448
        :param run_hooks: Private parameter - if false, this branch
3050
3449
            is being called because it's the master of the primary branch,
3051
3450
            so it should not run its hooks.
3052
 
        :param _override_hook_target: Private parameter - set the branch to be
3053
 
            supplied as the target_branch to pull hooks.
3054
 
        :param local: Only update the local branch, and not the bound branch.
3055
3451
        """
3056
 
        # This type of branch can't be bound.
3057
 
        if local:
 
3452
        bound_location = self.target.get_bound_location()
 
3453
        if local and not bound_location:
3058
3454
            raise errors.LocalRequiresBoundBranch()
3059
 
        result = PullResult()
3060
 
        result.source_branch = self.source
3061
 
        if _override_hook_target is None:
3062
 
            result.target_branch = self.target
3063
 
        else:
3064
 
            result.target_branch = _override_hook_target
3065
 
        self.source.lock_read()
 
3455
        master_branch = None
 
3456
        if not local and bound_location and self.source.user_url != bound_location:
 
3457
            # not pulling from master, so we need to update master.
 
3458
            master_branch = self.target.get_master_branch(possible_transports)
 
3459
            master_branch.lock_write()
3066
3460
        try:
3067
 
            # We assume that during 'pull' the target repository is closer than
3068
 
            # the source one.
3069
 
            self.source.update_references(self.target)
3070
 
            graph = self.target.repository.get_graph(self.source.repository)
3071
 
            # TODO: Branch formats should have a flag that indicates 
3072
 
            # that revno's are expensive, and pull() should honor that flag.
3073
 
            # -- JRV20090506
3074
 
            result.old_revno, result.old_revid = \
3075
 
                self.target.last_revision_info()
3076
 
            self.target.update_revisions(self.source, stop_revision,
3077
 
                overwrite=overwrite, graph=graph)
3078
 
            # TODO: The old revid should be specified when merging tags, 
3079
 
            # so a tags implementation that versions tags can only 
3080
 
            # pull in the most recent changes. -- JRV20090506
3081
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3082
 
                overwrite)
3083
 
            result.new_revno, result.new_revid = self.target.last_revision_info()
3084
 
            if _hook_master:
3085
 
                result.master_branch = _hook_master
3086
 
                result.local_branch = result.target_branch
3087
 
            else:
3088
 
                result.master_branch = result.target_branch
3089
 
                result.local_branch = None
3090
 
            if run_hooks:
3091
 
                for hook in Branch.hooks['post_pull']:
3092
 
                    hook(result)
 
3461
            if master_branch:
 
3462
                # pull from source into master.
 
3463
                master_branch.pull(self.source, overwrite, stop_revision,
 
3464
                    run_hooks=False)
 
3465
            return self._pull(overwrite,
 
3466
                stop_revision, _hook_master=master_branch,
 
3467
                run_hooks=run_hooks,
 
3468
                _override_hook_target=_override_hook_target)
3093
3469
        finally:
3094
 
            self.source.unlock()
3095
 
        return result
 
3470
            if master_branch:
 
3471
                master_branch.unlock()
3096
3472
 
3097
3473
    def push(self, overwrite=False, stop_revision=None,
3098
3474
             _override_hook_source_branch=None):
3138
3514
                # push into the master from the source branch.
3139
3515
                self.source._basic_push(master_branch, overwrite, stop_revision)
3140
3516
                # and push into the target branch from the source. Note that we
3141
 
                # push from the source branch again, because its considered the
 
3517
                # push from the source branch again, because it's considered the
3142
3518
                # highest bandwidth repository.
3143
3519
                result = self.source._basic_push(self.target, overwrite,
3144
3520
                    stop_revision)
3160
3536
            _run_hooks()
3161
3537
            return result
3162
3538
 
3163
 
    @classmethod
3164
 
    def is_compatible(self, source, target):
3165
 
        # GenericBranch uses the public API, so always compatible
3166
 
        return True
3167
 
 
3168
 
 
3169
 
class InterToBranch5(GenericInterBranch):
3170
 
 
3171
 
    @staticmethod
3172
 
    def _get_branch_formats_to_test():
3173
 
        return BranchFormat._default_format, BzrBranchFormat5()
3174
 
 
3175
 
    def pull(self, overwrite=False, stop_revision=None,
3176
 
             possible_transports=None, run_hooks=True,
 
3539
    def _pull(self, overwrite=False, stop_revision=None,
 
3540
             possible_transports=None, _hook_master=None, run_hooks=True,
3177
3541
             _override_hook_target=None, local=False):
3178
 
        """Pull from source into self, updating my master if any.
3179
 
 
 
3542
        """See Branch.pull.
 
3543
 
 
3544
        This function is the core worker, used by GenericInterBranch.pull to
 
3545
        avoid duplication when pulling source->master and source->local.
 
3546
 
 
3547
        :param _hook_master: Private parameter - set the branch to
 
3548
            be supplied as the master to pull hooks.
3180
3549
        :param run_hooks: Private parameter - if false, this branch
3181
3550
            is being called because it's the master of the primary branch,
3182
3551
            so it should not run its hooks.
 
3552
        :param _override_hook_target: Private parameter - set the branch to be
 
3553
            supplied as the target_branch to pull hooks.
 
3554
        :param local: Only update the local branch, and not the bound branch.
3183
3555
        """
3184
 
        bound_location = self.target.get_bound_location()
3185
 
        if local and not bound_location:
 
3556
        # This type of branch can't be bound.
 
3557
        if local:
3186
3558
            raise errors.LocalRequiresBoundBranch()
3187
 
        master_branch = None
3188
 
        if not local and bound_location and self.source.base != bound_location:
3189
 
            # not pulling from master, so we need to update master.
3190
 
            master_branch = self.target.get_master_branch(possible_transports)
3191
 
            master_branch.lock_write()
 
3559
        result = PullResult()
 
3560
        result.source_branch = self.source
 
3561
        if _override_hook_target is None:
 
3562
            result.target_branch = self.target
 
3563
        else:
 
3564
            result.target_branch = _override_hook_target
 
3565
        self.source.lock_read()
3192
3566
        try:
3193
 
            if master_branch:
3194
 
                # pull from source into master.
3195
 
                master_branch.pull(self.source, overwrite, stop_revision,
3196
 
                    run_hooks=False)
3197
 
            return super(InterToBranch5, self).pull(overwrite,
3198
 
                stop_revision, _hook_master=master_branch,
3199
 
                run_hooks=run_hooks,
3200
 
                _override_hook_target=_override_hook_target)
 
3567
            # We assume that during 'pull' the target repository is closer than
 
3568
            # the source one.
 
3569
            self.source.update_references(self.target)
 
3570
            graph = self.target.repository.get_graph(self.source.repository)
 
3571
            # TODO: Branch formats should have a flag that indicates 
 
3572
            # that revno's are expensive, and pull() should honor that flag.
 
3573
            # -- JRV20090506
 
3574
            result.old_revno, result.old_revid = \
 
3575
                self.target.last_revision_info()
 
3576
            self.target.update_revisions(self.source, stop_revision,
 
3577
                overwrite=overwrite, graph=graph)
 
3578
            # TODO: The old revid should be specified when merging tags, 
 
3579
            # so a tags implementation that versions tags can only 
 
3580
            # pull in the most recent changes. -- JRV20090506
 
3581
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3582
                overwrite)
 
3583
            result.new_revno, result.new_revid = self.target.last_revision_info()
 
3584
            if _hook_master:
 
3585
                result.master_branch = _hook_master
 
3586
                result.local_branch = result.target_branch
 
3587
            else:
 
3588
                result.master_branch = result.target_branch
 
3589
                result.local_branch = None
 
3590
            if run_hooks:
 
3591
                for hook in Branch.hooks['post_pull']:
 
3592
                    hook(result)
3201
3593
        finally:
3202
 
            if master_branch:
3203
 
                master_branch.unlock()
 
3594
            self.source.unlock()
 
3595
        return result
3204
3596
 
3205
3597
 
3206
3598
InterBranch.register_optimiser(GenericInterBranch)
3207
 
InterBranch.register_optimiser(InterToBranch5)