~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-23 19:10:35 UTC
  • mto: This revision was merged to the branch mainline in revision 5390.
  • Revision ID: john@arbash-meinel.com-20100823191035-57bojnmqw54nutsz
switch 'x += 1' to 'x = x + 1' to deal with brain-damaged old versions of pyrex.

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
29
29
        errors,
30
30
        lockdir,
31
31
        lockable_files,
 
32
        remote,
32
33
        repository,
33
34
        revision as _mod_revision,
34
35
        rio,
49
50
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
50
51
from bzrlib.hooks import HookPoint, Hooks
51
52
from bzrlib.inter import InterObject
52
 
from bzrlib.lock import _RelockDebugMixin
 
53
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
53
54
from bzrlib import registry
54
55
from bzrlib.symbol_versioning import (
55
56
    deprecated_in,
63
64
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
64
65
 
65
66
 
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):
 
67
class Branch(bzrdir.ControlComponent):
79
68
    """Branch holding a history of revisions.
80
69
 
81
 
    base
82
 
        Base directory/url of the branch.
 
70
    :ivar base:
 
71
        Base directory/url of the branch; using control_url and
 
72
        control_transport is more standardized.
83
73
 
84
74
    hooks: An instance of BranchHooks.
85
75
    """
87
77
    # - RBC 20060112
88
78
    base = None
89
79
 
 
80
    @property
 
81
    def control_transport(self):
 
82
        return self._transport
 
83
 
 
84
    @property
 
85
    def user_transport(self):
 
86
        return self.bzrdir.user_transport
 
87
 
90
88
    def __init__(self, *ignored, **ignored_too):
91
89
        self.tags = self._format.make_tags(self)
92
90
        self._revision_history_cache = None
107
105
        """Activate the branch/repository from url as a fallback repository."""
108
106
        repo = self._get_fallback_repository(url)
109
107
        if repo.has_same_location(self.repository):
110
 
            raise errors.UnstackableLocationError(self.base, url)
 
108
            raise errors.UnstackableLocationError(self.user_url, url)
111
109
        self.repository.add_fallback_repository(repo)
112
110
 
113
111
    def break_lock(self):
167
165
        """
168
166
        control = bzrdir.BzrDir.open(base, _unsupported,
169
167
                                     possible_transports=possible_transports)
170
 
        return control.open_branch(_unsupported)
 
168
        return control.open_branch(unsupported=_unsupported)
171
169
 
172
170
    @staticmethod
173
 
    def open_from_transport(transport, _unsupported=False):
 
171
    def open_from_transport(transport, name=None, _unsupported=False):
174
172
        """Open the branch rooted at transport"""
175
173
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
176
 
        return control.open_branch(_unsupported)
 
174
        return control.open_branch(name=name, unsupported=_unsupported)
177
175
 
178
176
    @staticmethod
179
177
    def open_containing(url, possible_transports=None):
200
198
        return self.supports_tags() and self.tags.get_tag_dict()
201
199
 
202
200
    def get_config(self):
 
201
        """Get a bzrlib.config.BranchConfig for this Branch.
 
202
 
 
203
        This can then be used to get and set configuration options for the
 
204
        branch.
 
205
 
 
206
        :return: A bzrlib.config.BranchConfig.
 
207
        """
203
208
        return BranchConfig(self)
204
209
 
205
210
    def _get_config(self):
217
222
    def _get_fallback_repository(self, url):
218
223
        """Get the repository we fallback to at url."""
219
224
        url = urlutils.join(self.base, url)
220
 
        a_bzrdir = bzrdir.BzrDir.open(url,
 
225
        a_branch = Branch.open(url,
221
226
            possible_transports=[self.bzrdir.root_transport])
222
 
        return a_bzrdir.open_branch().repository
 
227
        return a_branch.repository
223
228
 
224
229
    def _get_tags_bytes(self):
225
230
        """Get the bytes of a serialised tags dict.
241
246
        if not local and not config.has_explicit_nickname():
242
247
            try:
243
248
                master = self.get_master_branch(possible_transports)
 
249
                if master and self.user_url == master.user_url:
 
250
                    raise errors.RecursiveBind(self.user_url)
244
251
                if master is not None:
245
252
                    # return the master branch value
246
253
                    return master.nick
 
254
            except errors.RecursiveBind, e:
 
255
                raise e
247
256
            except errors.BzrError, e:
248
257
                # Silently fall back to local implicit nick if the master is
249
258
                # unavailable
286
295
        new_history.reverse()
287
296
        return new_history
288
297
 
289
 
    def lock_write(self):
 
298
    def lock_write(self, token=None):
 
299
        """Lock the branch for write operations.
 
300
 
 
301
        :param token: A token to permit reacquiring a previously held and
 
302
            preserved lock.
 
303
        :return: A BranchWriteLockResult.
 
304
        """
290
305
        raise NotImplementedError(self.lock_write)
291
306
 
292
307
    def lock_read(self):
 
308
        """Lock the branch for read operations.
 
309
 
 
310
        :return: A bzrlib.lock.LogicalLockResult.
 
311
        """
293
312
        raise NotImplementedError(self.lock_read)
294
313
 
295
314
    def unlock(self):
420
439
            * 'include' - the stop revision is the last item in the result
421
440
            * 'with-merges' - include the stop revision and all of its
422
441
              merged revisions in the result
 
442
            * 'with-merges-without-common-ancestry' - filter out revisions 
 
443
              that are in both ancestries
423
444
        :param direction: either 'reverse' or 'forward':
424
445
            * reverse means return the start_revision_id first, i.e.
425
446
              start at the most recent revision and go backwards in history
447
468
        # start_revision_id.
448
469
        if self._merge_sorted_revisions_cache is None:
449
470
            last_revision = self.last_revision()
450
 
            last_key = (last_revision,)
451
 
            known_graph = self.repository.revisions.get_known_graph_ancestry(
452
 
                [last_key])
 
471
            known_graph = self.repository.get_known_graph_ancestry(
 
472
                [last_revision])
453
473
            self._merge_sorted_revisions_cache = known_graph.merge_sort(
454
 
                last_key)
 
474
                last_revision)
455
475
        filtered = self._filter_merge_sorted_revisions(
456
476
            self._merge_sorted_revisions_cache, start_revision_id,
457
477
            stop_revision_id, stop_rule)
 
478
        # Make sure we don't return revisions that are not part of the
 
479
        # start_revision_id ancestry.
 
480
        filtered = self._filter_start_non_ancestors(filtered)
458
481
        if direction == 'reverse':
459
482
            return filtered
460
483
        if direction == 'forward':
497
520
                       node.end_of_merge)
498
521
                if rev_id == stop_revision_id:
499
522
                    return
 
523
        elif stop_rule == 'with-merges-without-common-ancestry':
 
524
            # We want to exclude all revisions that are already part of the
 
525
            # stop_revision_id ancestry.
 
526
            graph = self.repository.get_graph()
 
527
            ancestors = graph.find_unique_ancestors(start_revision_id,
 
528
                                                    [stop_revision_id])
 
529
            for node in rev_iter:
 
530
                rev_id = node.key[-1]
 
531
                if rev_id not in ancestors:
 
532
                    continue
 
533
                yield (rev_id, node.merge_depth, node.revno,
 
534
                       node.end_of_merge)
500
535
        elif stop_rule == 'with-merges':
501
536
            stop_rev = self.repository.get_revision(stop_revision_id)
502
537
            if stop_rev.parent_ids:
525
560
        else:
526
561
            raise ValueError('invalid stop_rule %r' % stop_rule)
527
562
 
 
563
    def _filter_start_non_ancestors(self, rev_iter):
 
564
        # If we started from a dotted revno, we want to consider it as a tip
 
565
        # and don't want to yield revisions that are not part of its
 
566
        # ancestry. Given the order guaranteed by the merge sort, we will see
 
567
        # uninteresting descendants of the first parent of our tip before the
 
568
        # tip itself.
 
569
        first = rev_iter.next()
 
570
        (rev_id, merge_depth, revno, end_of_merge) = first
 
571
        yield first
 
572
        if not merge_depth:
 
573
            # We start at a mainline revision so by definition, all others
 
574
            # revisions in rev_iter are ancestors
 
575
            for node in rev_iter:
 
576
                yield node
 
577
 
 
578
        clean = False
 
579
        whitelist = set()
 
580
        pmap = self.repository.get_parent_map([rev_id])
 
581
        parents = pmap.get(rev_id, [])
 
582
        if parents:
 
583
            whitelist.update(parents)
 
584
        else:
 
585
            # If there is no parents, there is nothing of interest left
 
586
 
 
587
            # FIXME: It's hard to test this scenario here as this code is never
 
588
            # called in that case. -- vila 20100322
 
589
            return
 
590
 
 
591
        for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
 
592
            if not clean:
 
593
                if rev_id in whitelist:
 
594
                    pmap = self.repository.get_parent_map([rev_id])
 
595
                    parents = pmap.get(rev_id, [])
 
596
                    whitelist.remove(rev_id)
 
597
                    whitelist.update(parents)
 
598
                    if merge_depth == 0:
 
599
                        # We've reached the mainline, there is nothing left to
 
600
                        # filter
 
601
                        clean = True
 
602
                else:
 
603
                    # A revision that is not part of the ancestry of our
 
604
                    # starting revision.
 
605
                    continue
 
606
            yield (rev_id, merge_depth, revno, end_of_merge)
 
607
 
528
608
    def leave_lock_in_place(self):
529
609
        """Tell this branch object not to release the physical lock when this
530
610
        object is unlocked.
547
627
        :param other: The branch to bind to
548
628
        :type other: Branch
549
629
        """
550
 
        raise errors.UpgradeRequired(self.base)
 
630
        raise errors.UpgradeRequired(self.user_url)
551
631
 
552
632
    def set_append_revisions_only(self, enabled):
553
633
        if not self._format.supports_set_append_revisions_only():
554
 
            raise errors.UpgradeRequired(self.base)
 
634
            raise errors.UpgradeRequired(self.user_url)
555
635
        if enabled:
556
636
            value = 'True'
557
637
        else:
605
685
    def get_old_bound_location(self):
606
686
        """Return the URL of the branch we used to be bound to
607
687
        """
608
 
        raise errors.UpgradeRequired(self.base)
 
688
        raise errors.UpgradeRequired(self.user_url)
609
689
 
610
690
    def get_commit_builder(self, parents, config=None, timestamp=None,
611
691
                           timezone=None, committer=None, revprops=None,
689
769
            stacking.
690
770
        """
691
771
        if not self._format.supports_stacking():
692
 
            raise errors.UnstackableBranchFormat(self._format, self.base)
 
772
            raise errors.UnstackableBranchFormat(self._format, self.user_url)
693
773
        # XXX: Changing from one fallback repository to another does not check
694
774
        # that all the data you need is present in the new fallback.
695
775
        # Possibly it should.
725
805
            if len(old_repository._fallback_repositories) != 1:
726
806
                raise AssertionError("can't cope with fallback repositories "
727
807
                    "of %r" % (self.repository,))
728
 
            # unlock it, including unlocking the fallback
 
808
            # Open the new repository object.
 
809
            # Repositories don't offer an interface to remove fallback
 
810
            # repositories today; take the conceptually simpler option and just
 
811
            # reopen it.  We reopen it starting from the URL so that we
 
812
            # get a separate connection for RemoteRepositories and can
 
813
            # stream from one of them to the other.  This does mean doing
 
814
            # separate SSH connection setup, but unstacking is not a
 
815
            # common operation so it's tolerable.
 
816
            new_bzrdir = bzrdir.BzrDir.open(self.bzrdir.root_transport.base)
 
817
            new_repository = new_bzrdir.find_repository()
 
818
            if new_repository._fallback_repositories:
 
819
                raise AssertionError("didn't expect %r to have "
 
820
                    "fallback_repositories"
 
821
                    % (self.repository,))
 
822
            # Replace self.repository with the new repository.
 
823
            # Do our best to transfer the lock state (i.e. lock-tokens and
 
824
            # lock count) of self.repository to the new repository.
 
825
            lock_token = old_repository.lock_write().repository_token
 
826
            self.repository = new_repository
 
827
            if isinstance(self, remote.RemoteBranch):
 
828
                # Remote branches can have a second reference to the old
 
829
                # repository that need to be replaced.
 
830
                if self._real_branch is not None:
 
831
                    self._real_branch.repository = new_repository
 
832
            self.repository.lock_write(token=lock_token)
 
833
            if lock_token is not None:
 
834
                old_repository.leave_lock_in_place()
729
835
            old_repository.unlock()
 
836
            if lock_token is not None:
 
837
                # XXX: self.repository.leave_lock_in_place() before this
 
838
                # function will not be preserved.  Fortunately that doesn't
 
839
                # affect the current default format (2a), and would be a
 
840
                # corner-case anyway.
 
841
                #  - Andrew Bennetts, 2010/06/30
 
842
                self.repository.dont_leave_lock_in_place()
 
843
            old_lock_count = 0
 
844
            while True:
 
845
                try:
 
846
                    old_repository.unlock()
 
847
                except errors.LockNotHeld:
 
848
                    break
 
849
                old_lock_count += 1
 
850
            if old_lock_count == 0:
 
851
                raise AssertionError(
 
852
                    'old_repository should have been locked at least once.')
 
853
            for i in range(old_lock_count-1):
 
854
                self.repository.lock_write()
 
855
            # Fetch from the old repository into the new.
730
856
            old_repository.lock_read()
731
857
            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
858
                # XXX: If you unstack a branch while it has a working tree
751
859
                # with a pending merge, the pending-merged revisions will no
752
860
                # longer be present.  You can (probably) revert and remerge.
846
954
 
847
955
    def unbind(self):
848
956
        """Older format branches cannot bind or unbind."""
849
 
        raise errors.UpgradeRequired(self.base)
 
957
        raise errors.UpgradeRequired(self.user_url)
850
958
 
851
959
    def last_revision(self):
852
960
        """Return last revision id, or NULL_REVISION."""
893
1001
                raise errors.NoSuchRevision(self, stop_revision)
894
1002
        return other_history[self_len:stop_revision]
895
1003
 
896
 
    @needs_write_lock
897
1004
    def update_revisions(self, other, stop_revision=None, overwrite=False,
898
1005
                         graph=None):
899
1006
        """Pull in new perfect-fit revisions.
948
1055
            self._extend_partial_history(distance_from_last)
949
1056
        return self._partial_revision_history_cache[distance_from_last]
950
1057
 
951
 
    @needs_write_lock
952
1058
    def pull(self, source, overwrite=False, stop_revision=None,
953
1059
             possible_transports=None, *args, **kwargs):
954
1060
        """Mirror source into this branch.
1012
1118
        try:
1013
1119
            return urlutils.join(self.base[:-1], parent)
1014
1120
        except errors.InvalidURLJoin, e:
1015
 
            raise errors.InaccessibleParent(parent, self.base)
 
1121
            raise errors.InaccessibleParent(parent, self.user_url)
1016
1122
 
1017
1123
    def _get_parent_location(self):
1018
1124
        raise NotImplementedError(self._get_parent_location)
1197
1303
                revno = 1
1198
1304
        destination.set_last_revision_info(revno, revision_id)
1199
1305
 
1200
 
    @needs_read_lock
1201
1306
    def copy_content_into(self, destination, revision_id=None):
1202
1307
        """Copy the content of self into destination.
1203
1308
 
1204
1309
        revision_id: if not None, the revision history in the new branch will
1205
1310
                     be truncated to end with revision_id.
1206
1311
        """
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)
 
1312
        return InterBranch.get(self, destination).copy_content_into(
 
1313
            revision_id=revision_id)
1218
1314
 
1219
1315
    def update_references(self, target):
1220
1316
        if not getattr(self._format, 'supports_reference_locations', False):
1288
1384
        """
1289
1385
        # XXX: Fix the bzrdir API to allow getting the branch back from the
1290
1386
        # clone call. Or something. 20090224 RBC/spiv.
 
1387
        # XXX: Should this perhaps clone colocated branches as well, 
 
1388
        # rather than just the default branch? 20100319 JRV
1291
1389
        if revision_id is None:
1292
1390
            revision_id = self.last_revision()
1293
1391
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1317
1415
        if lightweight:
1318
1416
            format = self._get_checkout_format()
1319
1417
            checkout = format.initialize_on_transport(t)
1320
 
            from_branch = BranchReferenceFormat().initialize(checkout, self)
 
1418
            from_branch = BranchReferenceFormat().initialize(checkout, 
 
1419
                target_branch=self)
1321
1420
        else:
1322
1421
            format = self._get_checkout_format()
1323
1422
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1365
1464
    def supports_tags(self):
1366
1465
        return self._format.supports_tags()
1367
1466
 
 
1467
    def automatic_tag_name(self, revision_id):
 
1468
        """Try to automatically find the tag name for a revision.
 
1469
 
 
1470
        :param revision_id: Revision id of the revision.
 
1471
        :return: A tag name or None if no tag name could be determined.
 
1472
        """
 
1473
        for hook in Branch.hooks['automatic_tag_name']:
 
1474
            ret = hook(self, revision_id)
 
1475
            if ret is not None:
 
1476
                return ret
 
1477
        return None
 
1478
 
1368
1479
    def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1369
1480
                                         other_branch):
1370
1481
        """Ensure that revision_b is a descendant of revision_a.
1434
1545
        return not (self == other)
1435
1546
 
1436
1547
    @classmethod
1437
 
    def find_format(klass, a_bzrdir):
 
1548
    def find_format(klass, a_bzrdir, name=None):
1438
1549
        """Return the format for the branch object in a_bzrdir."""
1439
1550
        try:
1440
 
            transport = a_bzrdir.get_branch_transport(None)
 
1551
            transport = a_bzrdir.get_branch_transport(None, name=name)
1441
1552
            format_string = transport.get_bytes("format")
1442
 
            return klass._formats[format_string]
 
1553
            format = klass._formats[format_string]
 
1554
            if isinstance(format, MetaDirBranchFormatFactory):
 
1555
                return format()
 
1556
            return format
1443
1557
        except errors.NoSuchFile:
1444
1558
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
1445
1559
        except KeyError:
1450
1564
        """Return the current default format."""
1451
1565
        return klass._default_format
1452
1566
 
1453
 
    def get_reference(self, a_bzrdir):
 
1567
    @classmethod
 
1568
    def get_formats(klass):
 
1569
        """Get all the known formats.
 
1570
 
 
1571
        Warning: This triggers a load of all lazy registered formats: do not
 
1572
        use except when that is desireed.
 
1573
        """
 
1574
        result = []
 
1575
        for fmt in klass._formats.values():
 
1576
            if isinstance(fmt, MetaDirBranchFormatFactory):
 
1577
                fmt = fmt()
 
1578
            result.append(fmt)
 
1579
        return result
 
1580
 
 
1581
    def get_reference(self, a_bzrdir, name=None):
1454
1582
        """Get the target reference of the branch in a_bzrdir.
1455
1583
 
1456
1584
        format probing must have been completed before calling
1458
1586
        in a_bzrdir is correct.
1459
1587
 
1460
1588
        :param a_bzrdir: The bzrdir to get the branch data from.
 
1589
        :param name: Name of the colocated branch to fetch
1461
1590
        :return: None if the branch is not a reference branch.
1462
1591
        """
1463
1592
        return None
1464
1593
 
1465
1594
    @classmethod
1466
 
    def set_reference(self, a_bzrdir, to_branch):
 
1595
    def set_reference(self, a_bzrdir, name, to_branch):
1467
1596
        """Set the target reference of the branch in a_bzrdir.
1468
1597
 
1469
1598
        format probing must have been completed before calling
1471
1600
        in a_bzrdir is correct.
1472
1601
 
1473
1602
        :param a_bzrdir: The bzrdir to set the branch reference for.
 
1603
        :param name: Name of colocated branch to set, None for default
1474
1604
        :param to_branch: branch that the checkout is to reference
1475
1605
        """
1476
1606
        raise NotImplementedError(self.set_reference)
1483
1613
        """Return the short format description for this format."""
1484
1614
        raise NotImplementedError(self.get_format_description)
1485
1615
 
1486
 
    def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
1487
 
                           set_format=True):
 
1616
    def _run_post_branch_init_hooks(self, a_bzrdir, name, branch):
 
1617
        hooks = Branch.hooks['post_branch_init']
 
1618
        if not hooks:
 
1619
            return
 
1620
        params = BranchInitHookParams(self, a_bzrdir, name, branch)
 
1621
        for hook in hooks:
 
1622
            hook(params)
 
1623
 
 
1624
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
1625
                           lock_type='metadir', set_format=True):
1488
1626
        """Initialize a branch in a bzrdir, with specified files
1489
1627
 
1490
1628
        :param a_bzrdir: The bzrdir to initialize the branch in
1491
1629
        :param utf8_files: The files to create as a list of
1492
1630
            (filename, content) tuples
 
1631
        :param name: Name of colocated branch to create, if any
1493
1632
        :param set_format: If True, set the format with
1494
1633
            self.get_format_string.  (BzrBranch4 has its format set
1495
1634
            elsewhere)
1496
1635
        :return: a branch in this format
1497
1636
        """
1498
 
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
1499
 
        branch_transport = a_bzrdir.get_branch_transport(self)
 
1637
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
1638
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1500
1639
        lock_map = {
1501
1640
            'metadir': ('lock', lockdir.LockDir),
1502
1641
            'branch4': ('branch-lock', lockable_files.TransportLock),
1523
1662
        finally:
1524
1663
            if lock_taken:
1525
1664
                control_files.unlock()
1526
 
        return self.open(a_bzrdir, _found=True)
 
1665
        branch = self.open(a_bzrdir, name, _found=True)
 
1666
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
1667
        return branch
1527
1668
 
1528
 
    def initialize(self, a_bzrdir):
1529
 
        """Create a branch of this format in a_bzrdir."""
 
1669
    def initialize(self, a_bzrdir, name=None):
 
1670
        """Create a branch of this format in a_bzrdir.
 
1671
        
 
1672
        :param name: Name of the colocated branch to create.
 
1673
        """
1530
1674
        raise NotImplementedError(self.initialize)
1531
1675
 
1532
1676
    def is_supported(self):
1562
1706
        """
1563
1707
        raise NotImplementedError(self.network_name)
1564
1708
 
1565
 
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
 
1709
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1566
1710
        """Return the branch object for a_bzrdir
1567
1711
 
1568
1712
        :param a_bzrdir: A BzrDir that contains a branch.
 
1713
        :param name: Name of colocated branch to open
1569
1714
        :param _found: a private parameter, do not use it. It is used to
1570
1715
            indicate if format probing has already be done.
1571
1716
        :param ignore_fallbacks: when set, no fallback branches will be opened
1575
1720
 
1576
1721
    @classmethod
1577
1722
    def register_format(klass, format):
1578
 
        """Register a metadir format."""
 
1723
        """Register a metadir format.
 
1724
        
 
1725
        See MetaDirBranchFormatFactory for the ability to register a format
 
1726
        without loading the code the format needs until it is actually used.
 
1727
        """
1579
1728
        klass._formats[format.get_format_string()] = format
1580
1729
        # 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__)
 
1730
        # registered as factories.
 
1731
        if isinstance(format, MetaDirBranchFormatFactory):
 
1732
            network_format_registry.register(format.get_format_string(), format)
 
1733
        else:
 
1734
            network_format_registry.register(format.get_format_string(),
 
1735
                format.__class__)
1583
1736
 
1584
1737
    @classmethod
1585
1738
    def set_default_format(klass, format):
1605
1758
        return False  # by default
1606
1759
 
1607
1760
 
 
1761
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
 
1762
    """A factory for a BranchFormat object, permitting simple lazy registration.
 
1763
    
 
1764
    While none of the built in BranchFormats are lazy registered yet,
 
1765
    bzrlib.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
 
1766
    use it, and the bzr-loom plugin uses it as well (see
 
1767
    bzrlib.plugins.loom.formats).
 
1768
    """
 
1769
 
 
1770
    def __init__(self, format_string, module_name, member_name):
 
1771
        """Create a MetaDirBranchFormatFactory.
 
1772
 
 
1773
        :param format_string: The format string the format has.
 
1774
        :param module_name: Module to load the format class from.
 
1775
        :param member_name: Attribute name within the module for the format class.
 
1776
        """
 
1777
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
 
1778
        self._format_string = format_string
 
1779
        
 
1780
    def get_format_string(self):
 
1781
        """See BranchFormat.get_format_string."""
 
1782
        return self._format_string
 
1783
 
 
1784
    def __call__(self):
 
1785
        """Used for network_format_registry support."""
 
1786
        return self.get_obj()()
 
1787
 
 
1788
 
1608
1789
class BranchHooks(Hooks):
1609
1790
    """A dictionary mapping hook name to a list of callables for branch hooks.
1610
1791
 
1679
1860
            "multiple hooks installed for transform_fallback_location, "
1680
1861
            "all are called with the url returned from the previous hook."
1681
1862
            "The order is however undefined.", (1, 9), None))
 
1863
        self.create_hook(HookPoint('automatic_tag_name',
 
1864
            "Called to determine an automatic tag name for a revision. "
 
1865
            "automatic_tag_name is called with (branch, revision_id) and "
 
1866
            "should return a tag name or None if no tag name could be "
 
1867
            "determined. The first non-None tag name returned will be used.",
 
1868
            (2, 2), None))
 
1869
        self.create_hook(HookPoint('post_branch_init',
 
1870
            "Called after new branch initialization completes. "
 
1871
            "post_branch_init is called with a "
 
1872
            "bzrlib.branch.BranchInitHookParams. "
 
1873
            "Note that init, branch and checkout (both heavyweight and "
 
1874
            "lightweight) will all trigger this hook.", (2, 2), None))
 
1875
        self.create_hook(HookPoint('post_switch',
 
1876
            "Called after a checkout switches branch. "
 
1877
            "post_switch is called with a "
 
1878
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
 
1879
 
1682
1880
 
1683
1881
 
1684
1882
# install the default hooks into the Branch class.
1723
1921
            self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1724
1922
 
1725
1923
 
 
1924
class BranchInitHookParams(object):
 
1925
    """Object holding parameters passed to *_branch_init hooks.
 
1926
 
 
1927
    There are 4 fields that hooks may wish to access:
 
1928
 
 
1929
    :ivar format: the branch format
 
1930
    :ivar bzrdir: the BzrDir where the branch will be/has been initialized
 
1931
    :ivar name: name of colocated branch, if any (or None)
 
1932
    :ivar branch: the branch created
 
1933
 
 
1934
    Note that for lightweight checkouts, the bzrdir and format fields refer to
 
1935
    the checkout, hence they are different from the corresponding fields in
 
1936
    branch, which refer to the original branch.
 
1937
    """
 
1938
 
 
1939
    def __init__(self, format, a_bzrdir, name, branch):
 
1940
        """Create a group of BranchInitHook parameters.
 
1941
 
 
1942
        :param format: the branch format
 
1943
        :param a_bzrdir: the BzrDir where the branch will be/has been
 
1944
            initialized
 
1945
        :param name: name of colocated branch, if any (or None)
 
1946
        :param branch: the branch created
 
1947
 
 
1948
        Note that for lightweight checkouts, the bzrdir and format fields refer
 
1949
        to the checkout, hence they are different from the corresponding fields
 
1950
        in branch, which refer to the original branch.
 
1951
        """
 
1952
        self.format = format
 
1953
        self.bzrdir = a_bzrdir
 
1954
        self.name = name
 
1955
        self.branch = branch
 
1956
 
 
1957
    def __eq__(self, other):
 
1958
        return self.__dict__ == other.__dict__
 
1959
 
 
1960
    def __repr__(self):
 
1961
        return "<%s of %s>" % (self.__class__.__name__, self.branch)
 
1962
 
 
1963
 
 
1964
class SwitchHookParams(object):
 
1965
    """Object holding parameters passed to *_switch hooks.
 
1966
 
 
1967
    There are 4 fields that hooks may wish to access:
 
1968
 
 
1969
    :ivar control_dir: BzrDir of the checkout to change
 
1970
    :ivar to_branch: branch that the checkout is to reference
 
1971
    :ivar force: skip the check for local commits in a heavy checkout
 
1972
    :ivar revision_id: revision ID to switch to (or None)
 
1973
    """
 
1974
 
 
1975
    def __init__(self, control_dir, to_branch, force, revision_id):
 
1976
        """Create a group of SwitchHook parameters.
 
1977
 
 
1978
        :param control_dir: BzrDir of the checkout to change
 
1979
        :param to_branch: branch that the checkout is to reference
 
1980
        :param force: skip the check for local commits in a heavy checkout
 
1981
        :param revision_id: revision ID to switch to (or None)
 
1982
        """
 
1983
        self.control_dir = control_dir
 
1984
        self.to_branch = to_branch
 
1985
        self.force = force
 
1986
        self.revision_id = revision_id
 
1987
 
 
1988
    def __eq__(self, other):
 
1989
        return self.__dict__ == other.__dict__
 
1990
 
 
1991
    def __repr__(self):
 
1992
        return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
 
1993
            self.control_dir, self.to_branch,
 
1994
            self.revision_id)
 
1995
 
 
1996
 
1726
1997
class BzrBranchFormat4(BranchFormat):
1727
1998
    """Bzr branch format 4.
1728
1999
 
1735
2006
        """See BranchFormat.get_format_description()."""
1736
2007
        return "Branch format 4"
1737
2008
 
1738
 
    def initialize(self, a_bzrdir):
 
2009
    def initialize(self, a_bzrdir, name=None):
1739
2010
        """Create a branch of this format in a_bzrdir."""
1740
2011
        utf8_files = [('revision-history', ''),
1741
2012
                      ('branch-name', ''),
1742
2013
                      ]
1743
 
        return self._initialize_helper(a_bzrdir, utf8_files,
 
2014
        return self._initialize_helper(a_bzrdir, utf8_files, name=name,
1744
2015
                                       lock_type='branch4', set_format=False)
1745
2016
 
1746
2017
    def __init__(self):
1751
2022
        """The network name for this format is the control dirs disk label."""
1752
2023
        return self._matchingbzrdir.get_format_string()
1753
2024
 
1754
 
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
 
2025
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1755
2026
        """See BranchFormat.open()."""
1756
2027
        if not _found:
1757
2028
            # we are being called directly and must probe.
1759
2030
        return BzrBranch(_format=self,
1760
2031
                         _control_files=a_bzrdir._control_files,
1761
2032
                         a_bzrdir=a_bzrdir,
 
2033
                         name=name,
1762
2034
                         _repository=a_bzrdir.open_repository())
1763
2035
 
1764
2036
    def __str__(self):
1779
2051
        """
1780
2052
        return self.get_format_string()
1781
2053
 
1782
 
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
 
2054
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False):
1783
2055
        """See BranchFormat.open()."""
1784
2056
        if not _found:
1785
 
            format = BranchFormat.find_format(a_bzrdir)
 
2057
            format = BranchFormat.find_format(a_bzrdir, name=name)
1786
2058
            if format.__class__ != self.__class__:
1787
2059
                raise AssertionError("wrong format %r found for %r" %
1788
2060
                    (format, self))
 
2061
        transport = a_bzrdir.get_branch_transport(None, name=name)
1789
2062
        try:
1790
 
            transport = a_bzrdir.get_branch_transport(None)
1791
2063
            control_files = lockable_files.LockableFiles(transport, 'lock',
1792
2064
                                                         lockdir.LockDir)
1793
2065
            return self._branch_class()(_format=self,
1794
2066
                              _control_files=control_files,
 
2067
                              name=name,
1795
2068
                              a_bzrdir=a_bzrdir,
1796
2069
                              _repository=a_bzrdir.find_repository(),
1797
2070
                              ignore_fallbacks=ignore_fallbacks)
1831
2104
        """See BranchFormat.get_format_description()."""
1832
2105
        return "Branch format 5"
1833
2106
 
1834
 
    def initialize(self, a_bzrdir):
 
2107
    def initialize(self, a_bzrdir, name=None):
1835
2108
        """Create a branch of this format in a_bzrdir."""
1836
2109
        utf8_files = [('revision-history', ''),
1837
2110
                      ('branch-name', ''),
1838
2111
                      ]
1839
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
2112
        return self._initialize_helper(a_bzrdir, utf8_files, name)
1840
2113
 
1841
2114
    def supports_tags(self):
1842
2115
        return False
1864
2137
        """See BranchFormat.get_format_description()."""
1865
2138
        return "Branch format 6"
1866
2139
 
1867
 
    def initialize(self, a_bzrdir):
 
2140
    def initialize(self, a_bzrdir, name=None):
1868
2141
        """Create a branch of this format in a_bzrdir."""
1869
2142
        utf8_files = [('last-revision', '0 null:\n'),
1870
2143
                      ('branch.conf', ''),
1871
2144
                      ('tags', ''),
1872
2145
                      ]
1873
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
2146
        return self._initialize_helper(a_bzrdir, utf8_files, name)
1874
2147
 
1875
2148
    def make_tags(self, branch):
1876
2149
        """See bzrlib.branch.BranchFormat.make_tags()."""
1894
2167
        """See BranchFormat.get_format_description()."""
1895
2168
        return "Branch format 8"
1896
2169
 
1897
 
    def initialize(self, a_bzrdir):
 
2170
    def initialize(self, a_bzrdir, name=None):
1898
2171
        """Create a branch of this format in a_bzrdir."""
1899
2172
        utf8_files = [('last-revision', '0 null:\n'),
1900
2173
                      ('branch.conf', ''),
1901
2174
                      ('tags', ''),
1902
2175
                      ('references', '')
1903
2176
                      ]
1904
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
2177
        return self._initialize_helper(a_bzrdir, utf8_files, name)
1905
2178
 
1906
2179
    def __init__(self):
1907
2180
        super(BzrBranchFormat8, self).__init__()
1930
2203
    This format was introduced in bzr 1.6.
1931
2204
    """
1932
2205
 
1933
 
    def initialize(self, a_bzrdir):
 
2206
    def initialize(self, a_bzrdir, name=None):
1934
2207
        """Create a branch of this format in a_bzrdir."""
1935
2208
        utf8_files = [('last-revision', '0 null:\n'),
1936
2209
                      ('branch.conf', ''),
1937
2210
                      ('tags', ''),
1938
2211
                      ]
1939
 
        return self._initialize_helper(a_bzrdir, utf8_files)
 
2212
        return self._initialize_helper(a_bzrdir, utf8_files, name)
1940
2213
 
1941
2214
    def _branch_class(self):
1942
2215
        return BzrBranch7
1974
2247
        """See BranchFormat.get_format_description()."""
1975
2248
        return "Checkout reference format 1"
1976
2249
 
1977
 
    def get_reference(self, a_bzrdir):
 
2250
    def get_reference(self, a_bzrdir, name=None):
1978
2251
        """See BranchFormat.get_reference()."""
1979
 
        transport = a_bzrdir.get_branch_transport(None)
 
2252
        transport = a_bzrdir.get_branch_transport(None, name=name)
1980
2253
        return transport.get_bytes('location')
1981
2254
 
1982
 
    def set_reference(self, a_bzrdir, to_branch):
 
2255
    def set_reference(self, a_bzrdir, name, to_branch):
1983
2256
        """See BranchFormat.set_reference()."""
1984
 
        transport = a_bzrdir.get_branch_transport(None)
 
2257
        transport = a_bzrdir.get_branch_transport(None, name=name)
1985
2258
        location = transport.put_bytes('location', to_branch.base)
1986
2259
 
1987
 
    def initialize(self, a_bzrdir, target_branch=None):
 
2260
    def initialize(self, a_bzrdir, name=None, target_branch=None):
1988
2261
        """Create a branch of this format in a_bzrdir."""
1989
2262
        if target_branch is None:
1990
2263
            # this format does not implement branch itself, thus the implicit
1991
2264
            # creation contract must see it as uninitializable
1992
2265
            raise errors.UninitializableFormat(self)
1993
 
        mutter('creating branch reference in %s', a_bzrdir.transport.base)
1994
 
        branch_transport = a_bzrdir.get_branch_transport(self)
 
2266
        mutter('creating branch reference in %s', a_bzrdir.user_url)
 
2267
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1995
2268
        branch_transport.put_bytes('location',
1996
 
            target_branch.bzrdir.root_transport.base)
 
2269
            target_branch.bzrdir.user_url)
1997
2270
        branch_transport.put_bytes('format', self.get_format_string())
1998
 
        return self.open(
1999
 
            a_bzrdir, _found=True,
 
2271
        branch = self.open(
 
2272
            a_bzrdir, name, _found=True,
2000
2273
            possible_transports=[target_branch.bzrdir.root_transport])
 
2274
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
2275
        return branch
2001
2276
 
2002
2277
    def __init__(self):
2003
2278
        super(BranchReferenceFormat, self).__init__()
2009
2284
        def clone(to_bzrdir, revision_id=None,
2010
2285
            repository_policy=None):
2011
2286
            """See Branch.clone()."""
2012
 
            return format.initialize(to_bzrdir, a_branch)
 
2287
            return format.initialize(to_bzrdir, target_branch=a_branch)
2013
2288
            # cannot obey revision_id limits when cloning a reference ...
2014
2289
            # FIXME RBC 20060210 either nuke revision_id for clone, or
2015
2290
            # emit some sort of warning/error to the caller ?!
2016
2291
        return clone
2017
2292
 
2018
 
    def open(self, a_bzrdir, _found=False, location=None,
 
2293
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2019
2294
             possible_transports=None, ignore_fallbacks=False):
2020
2295
        """Return the branch that the branch reference in a_bzrdir points at.
2021
2296
 
2022
2297
        :param a_bzrdir: A BzrDir that contains a branch.
 
2298
        :param name: Name of colocated branch to open, if any
2023
2299
        :param _found: a private parameter, do not use it. It is used to
2024
2300
            indicate if format probing has already be done.
2025
2301
        :param ignore_fallbacks: when set, no fallback branches will be opened
2030
2306
        :param possible_transports: An optional reusable transports list.
2031
2307
        """
2032
2308
        if not _found:
2033
 
            format = BranchFormat.find_format(a_bzrdir)
 
2309
            format = BranchFormat.find_format(a_bzrdir, name=name)
2034
2310
            if format.__class__ != self.__class__:
2035
2311
                raise AssertionError("wrong format %r found for %r" %
2036
2312
                    (format, self))
2037
2313
        if location is None:
2038
 
            location = self.get_reference(a_bzrdir)
 
2314
            location = self.get_reference(a_bzrdir, name)
2039
2315
        real_bzrdir = bzrdir.BzrDir.open(
2040
2316
            location, possible_transports=possible_transports)
2041
 
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
 
2317
        result = real_bzrdir.open_branch(name=name, 
 
2318
            ignore_fallbacks=ignore_fallbacks)
2042
2319
        # this changes the behaviour of result.clone to create a new reference
2043
2320
        # rather than a copy of the content of the branch.
2044
2321
        # I did not use a proxy object because that needs much more extensive
2078
2355
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2079
2356
 
2080
2357
 
 
2358
class BranchWriteLockResult(LogicalLockResult):
 
2359
    """The result of write locking a branch.
 
2360
 
 
2361
    :ivar branch_token: The token obtained from the underlying branch lock, or
 
2362
        None.
 
2363
    :ivar unlock: A callable which will unlock the lock.
 
2364
    """
 
2365
 
 
2366
    def __init__(self, unlock, branch_token):
 
2367
        LogicalLockResult.__init__(self, unlock)
 
2368
        self.branch_token = branch_token
 
2369
 
 
2370
    def __repr__(self):
 
2371
        return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
 
2372
            self.unlock)
 
2373
 
 
2374
 
2081
2375
class BzrBranch(Branch, _RelockDebugMixin):
2082
2376
    """A branch stored in the actual filesystem.
2083
2377
 
2090
2384
    :ivar repository: Repository for this branch.
2091
2385
    :ivar base: The url of the base directory for this branch; the one
2092
2386
        containing the .bzr directory.
 
2387
    :ivar name: Optional colocated branch name as it exists in the control
 
2388
        directory.
2093
2389
    """
2094
2390
 
2095
2391
    def __init__(self, _format=None,
2096
 
                 _control_files=None, a_bzrdir=None, _repository=None,
2097
 
                 ignore_fallbacks=False):
 
2392
                 _control_files=None, a_bzrdir=None, name=None,
 
2393
                 _repository=None, ignore_fallbacks=False):
2098
2394
        """Create new branch object at a particular location."""
2099
2395
        if a_bzrdir is None:
2100
2396
            raise ValueError('a_bzrdir must be supplied')
2101
2397
        else:
2102
2398
            self.bzrdir = a_bzrdir
2103
2399
        self._base = self.bzrdir.transport.clone('..').base
 
2400
        self.name = name
2104
2401
        # XXX: We should be able to just do
2105
2402
        #   self.base = self.bzrdir.root_transport.base
2106
2403
        # but this does not quite work yet -- mbp 20080522
2113
2410
        Branch.__init__(self)
2114
2411
 
2115
2412
    def __str__(self):
2116
 
        return '%s(%r)' % (self.__class__.__name__, self.base)
 
2413
        if self.name is None:
 
2414
            return '%s(%s)' % (self.__class__.__name__, self.user_url)
 
2415
        else:
 
2416
            return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
 
2417
                self.name)
2117
2418
 
2118
2419
    __repr__ = __str__
2119
2420
 
2130
2431
        return self.control_files.is_locked()
2131
2432
 
2132
2433
    def lock_write(self, token=None):
 
2434
        """Lock the branch for write operations.
 
2435
 
 
2436
        :param token: A token to permit reacquiring a previously held and
 
2437
            preserved lock.
 
2438
        :return: A BranchWriteLockResult.
 
2439
        """
2133
2440
        if not self.is_locked():
2134
2441
            self._note_lock('w')
2135
2442
        # All-in-one needs to always unlock/lock.
2141
2448
        else:
2142
2449
            took_lock = False
2143
2450
        try:
2144
 
            return self.control_files.lock_write(token=token)
 
2451
            return BranchWriteLockResult(self.unlock,
 
2452
                self.control_files.lock_write(token=token))
2145
2453
        except:
2146
2454
            if took_lock:
2147
2455
                self.repository.unlock()
2148
2456
            raise
2149
2457
 
2150
2458
    def lock_read(self):
 
2459
        """Lock the branch for read operations.
 
2460
 
 
2461
        :return: A bzrlib.lock.LogicalLockResult.
 
2462
        """
2151
2463
        if not self.is_locked():
2152
2464
            self._note_lock('r')
2153
2465
        # All-in-one needs to always unlock/lock.
2160
2472
            took_lock = False
2161
2473
        try:
2162
2474
            self.control_files.lock_read()
 
2475
            return LogicalLockResult(self.unlock)
2163
2476
        except:
2164
2477
            if took_lock:
2165
2478
                self.repository.unlock()
2334
2647
        return result
2335
2648
 
2336
2649
    def get_stacked_on_url(self):
2337
 
        raise errors.UnstackableBranchFormat(self._format, self.base)
 
2650
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2338
2651
 
2339
2652
    def set_push_location(self, location):
2340
2653
        """See Branch.set_push_location."""
2530
2843
        if _mod_revision.is_null(last_revision):
2531
2844
            return
2532
2845
        if last_revision not in self._lefthand_history(revision_id):
2533
 
            raise errors.AppendRevisionsOnlyViolation(self.base)
 
2846
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
2534
2847
 
2535
2848
    def _gen_revision_history(self):
2536
2849
        """Generate the revision history from last revision
2636
2949
        if branch_location is None:
2637
2950
            return Branch.reference_parent(self, file_id, path,
2638
2951
                                           possible_transports)
2639
 
        branch_location = urlutils.join(self.base, branch_location)
 
2952
        branch_location = urlutils.join(self.user_url, branch_location)
2640
2953
        return Branch.open(branch_location,
2641
2954
                           possible_transports=possible_transports)
2642
2955
 
2688
3001
        return stacked_url
2689
3002
 
2690
3003
    def _get_append_revisions_only(self):
2691
 
        value = self.get_config().get_user_option('append_revisions_only')
2692
 
        return value == 'True'
 
3004
        return self.get_config(
 
3005
            ).get_user_option_as_bool('append_revisions_only')
2693
3006
 
2694
3007
    @needs_write_lock
2695
3008
    def generate_revision_history(self, revision_id, last_rev=None,
2757
3070
    """
2758
3071
 
2759
3072
    def get_stacked_on_url(self):
2760
 
        raise errors.UnstackableBranchFormat(self._format, self.base)
 
3073
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2761
3074
 
2762
3075
 
2763
3076
######################################################################
2850
3163
        :param verbose: Requests more detailed display of what was checked,
2851
3164
            if any.
2852
3165
        """
2853
 
        note('checked branch %s format %s', self.branch.base,
 
3166
        note('checked branch %s format %s', self.branch.user_url,
2854
3167
            self.branch._format)
2855
3168
        for error in self.errors:
2856
3169
            note('found error:%s', error)
2951
3264
    _optimisers = []
2952
3265
    """The available optimised InterBranch types."""
2953
3266
 
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)
 
3267
    @classmethod
 
3268
    def _get_branch_formats_to_test(klass):
 
3269
        """Return an iterable of format tuples for testing.
 
3270
        
 
3271
        :return: An iterable of (from_format, to_format) to use when testing
 
3272
            this InterBranch class. Each InterBranch class should define this
 
3273
            method itself.
 
3274
        """
 
3275
        raise NotImplementedError(klass._get_branch_formats_to_test)
2958
3276
 
 
3277
    @needs_write_lock
2959
3278
    def pull(self, overwrite=False, stop_revision=None,
2960
3279
             possible_transports=None, local=False):
2961
3280
        """Mirror source into target branch.
2966
3285
        """
2967
3286
        raise NotImplementedError(self.pull)
2968
3287
 
 
3288
    @needs_write_lock
2969
3289
    def update_revisions(self, stop_revision=None, overwrite=False,
2970
3290
                         graph=None):
2971
3291
        """Pull in new perfect-fit revisions.
2979
3299
        """
2980
3300
        raise NotImplementedError(self.update_revisions)
2981
3301
 
 
3302
    @needs_write_lock
2982
3303
    def push(self, overwrite=False, stop_revision=None,
2983
3304
             _override_hook_source_branch=None):
2984
3305
        """Mirror the source branch into the target branch.
2987
3308
        """
2988
3309
        raise NotImplementedError(self.push)
2989
3310
 
 
3311
    @needs_write_lock
 
3312
    def copy_content_into(self, revision_id=None):
 
3313
        """Copy the content of source into target
 
3314
 
 
3315
        revision_id: if not None, the revision history in the new branch will
 
3316
                     be truncated to end with revision_id.
 
3317
        """
 
3318
        raise NotImplementedError(self.copy_content_into)
 
3319
 
2990
3320
 
2991
3321
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
 
 
 
3322
    """InterBranch implementation that uses public Branch functions."""
 
3323
 
 
3324
    @classmethod
 
3325
    def is_compatible(klass, source, target):
 
3326
        # GenericBranch uses the public API, so always compatible
 
3327
        return True
 
3328
 
 
3329
    @classmethod
 
3330
    def _get_branch_formats_to_test(klass):
 
3331
        return [(BranchFormat._default_format, BranchFormat._default_format)]
 
3332
 
 
3333
    @classmethod
 
3334
    def unwrap_format(klass, format):
 
3335
        if isinstance(format, remote.RemoteBranchFormat):
 
3336
            format._ensure_real()
 
3337
            return format._custom_format
 
3338
        return format                                                                                                  
 
3339
 
 
3340
    @needs_write_lock
 
3341
    def copy_content_into(self, revision_id=None):
 
3342
        """Copy the content of source into target
 
3343
 
 
3344
        revision_id: if not None, the revision history in the new branch will
 
3345
                     be truncated to end with revision_id.
 
3346
        """
 
3347
        self.source.update_references(self.target)
 
3348
        self.source._synchronize_history(self.target, revision_id)
 
3349
        try:
 
3350
            parent = self.source.get_parent()
 
3351
        except errors.InaccessibleParent, e:
 
3352
            mutter('parent was not accessible to copy: %s', e)
 
3353
        else:
 
3354
            if parent:
 
3355
                self.target.set_parent(parent)
 
3356
        if self.source._push_should_merge_tags():
 
3357
            self.source.tags.merge_to(self.target.tags)
 
3358
 
 
3359
    @needs_write_lock
2999
3360
    def update_revisions(self, stop_revision=None, overwrite=False,
3000
3361
        graph=None):
3001
3362
        """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
 
 
 
3363
        other_revno, other_last_revision = self.source.last_revision_info()
 
3364
        stop_revno = None # unknown
 
3365
        if stop_revision is None:
 
3366
            stop_revision = other_last_revision
 
3367
            if _mod_revision.is_null(stop_revision):
 
3368
                # if there are no commits, we're done.
 
3369
                return
 
3370
            stop_revno = other_revno
 
3371
 
 
3372
        # what's the current last revision, before we fetch [and change it
 
3373
        # possibly]
 
3374
        last_rev = _mod_revision.ensure_null(self.target.last_revision())
 
3375
        # we fetch here so that we don't process data twice in the common
 
3376
        # case of having something to pull, and so that the check for
 
3377
        # already merged can operate on the just fetched graph, which will
 
3378
        # be cached in memory.
 
3379
        self.target.fetch(self.source, stop_revision)
 
3380
        # Check to see if one is an ancestor of the other
 
3381
        if not overwrite:
 
3382
            if graph is None:
 
3383
                graph = self.target.repository.get_graph()
 
3384
            if self.target._check_if_descendant_or_diverged(
 
3385
                    stop_revision, last_rev, graph, self.source):
 
3386
                # stop_revision is a descendant of last_rev, but we aren't
 
3387
                # overwriting, so we're done.
 
3388
                return
 
3389
        if stop_revno is None:
 
3390
            if graph is None:
 
3391
                graph = self.target.repository.get_graph()
 
3392
            this_revno, this_last_revision = \
 
3393
                    self.target.last_revision_info()
 
3394
            stop_revno = graph.find_distance_to_null(stop_revision,
 
3395
                            [(other_last_revision, other_revno),
 
3396
                             (this_last_revision, this_revno)])
 
3397
        self.target.set_last_revision_info(stop_revno, stop_revision)
 
3398
 
 
3399
    @needs_write_lock
3042
3400
    def pull(self, overwrite=False, stop_revision=None,
3043
 
             possible_transports=None, _hook_master=None, run_hooks=True,
 
3401
             possible_transports=None, run_hooks=True,
3044
3402
             _override_hook_target=None, local=False):
3045
 
        """See Branch.pull.
 
3403
        """Pull from source into self, updating my master if any.
3046
3404
 
3047
 
        :param _hook_master: Private parameter - set the branch to
3048
 
            be supplied as the master to pull hooks.
3049
3405
        :param run_hooks: Private parameter - if false, this branch
3050
3406
            is being called because it's the master of the primary branch,
3051
3407
            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
3408
        """
3056
 
        # This type of branch can't be bound.
3057
 
        if local:
 
3409
        bound_location = self.target.get_bound_location()
 
3410
        if local and not bound_location:
3058
3411
            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()
 
3412
        master_branch = None
 
3413
        if not local and bound_location and self.source.user_url != bound_location:
 
3414
            # not pulling from master, so we need to update master.
 
3415
            master_branch = self.target.get_master_branch(possible_transports)
 
3416
            master_branch.lock_write()
3066
3417
        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)
 
3418
            if master_branch:
 
3419
                # pull from source into master.
 
3420
                master_branch.pull(self.source, overwrite, stop_revision,
 
3421
                    run_hooks=False)
 
3422
            return self._pull(overwrite,
 
3423
                stop_revision, _hook_master=master_branch,
 
3424
                run_hooks=run_hooks,
 
3425
                _override_hook_target=_override_hook_target)
3093
3426
        finally:
3094
 
            self.source.unlock()
3095
 
        return result
 
3427
            if master_branch:
 
3428
                master_branch.unlock()
3096
3429
 
3097
3430
    def push(self, overwrite=False, stop_revision=None,
3098
3431
             _override_hook_source_branch=None):
3160
3493
            _run_hooks()
3161
3494
            return result
3162
3495
 
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,
 
3496
    def _pull(self, overwrite=False, stop_revision=None,
 
3497
             possible_transports=None, _hook_master=None, run_hooks=True,
3177
3498
             _override_hook_target=None, local=False):
3178
 
        """Pull from source into self, updating my master if any.
3179
 
 
 
3499
        """See Branch.pull.
 
3500
 
 
3501
        This function is the core worker, used by GenericInterBranch.pull to
 
3502
        avoid duplication when pulling source->master and source->local.
 
3503
 
 
3504
        :param _hook_master: Private parameter - set the branch to
 
3505
            be supplied as the master to pull hooks.
3180
3506
        :param run_hooks: Private parameter - if false, this branch
3181
3507
            is being called because it's the master of the primary branch,
3182
3508
            so it should not run its hooks.
 
3509
        :param _override_hook_target: Private parameter - set the branch to be
 
3510
            supplied as the target_branch to pull hooks.
 
3511
        :param local: Only update the local branch, and not the bound branch.
3183
3512
        """
3184
 
        bound_location = self.target.get_bound_location()
3185
 
        if local and not bound_location:
 
3513
        # This type of branch can't be bound.
 
3514
        if local:
3186
3515
            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()
 
3516
        result = PullResult()
 
3517
        result.source_branch = self.source
 
3518
        if _override_hook_target is None:
 
3519
            result.target_branch = self.target
 
3520
        else:
 
3521
            result.target_branch = _override_hook_target
 
3522
        self.source.lock_read()
3192
3523
        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)
 
3524
            # We assume that during 'pull' the target repository is closer than
 
3525
            # the source one.
 
3526
            self.source.update_references(self.target)
 
3527
            graph = self.target.repository.get_graph(self.source.repository)
 
3528
            # TODO: Branch formats should have a flag that indicates 
 
3529
            # that revno's are expensive, and pull() should honor that flag.
 
3530
            # -- JRV20090506
 
3531
            result.old_revno, result.old_revid = \
 
3532
                self.target.last_revision_info()
 
3533
            self.target.update_revisions(self.source, stop_revision,
 
3534
                overwrite=overwrite, graph=graph)
 
3535
            # TODO: The old revid should be specified when merging tags, 
 
3536
            # so a tags implementation that versions tags can only 
 
3537
            # pull in the most recent changes. -- JRV20090506
 
3538
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3539
                overwrite)
 
3540
            result.new_revno, result.new_revid = self.target.last_revision_info()
 
3541
            if _hook_master:
 
3542
                result.master_branch = _hook_master
 
3543
                result.local_branch = result.target_branch
 
3544
            else:
 
3545
                result.master_branch = result.target_branch
 
3546
                result.local_branch = None
 
3547
            if run_hooks:
 
3548
                for hook in Branch.hooks['post_pull']:
 
3549
                    hook(result)
3201
3550
        finally:
3202
 
            if master_branch:
3203
 
                master_branch.unlock()
 
3551
            self.source.unlock()
 
3552
        return result
3204
3553
 
3205
3554
 
3206
3555
InterBranch.register_optimiser(GenericInterBranch)
3207
 
InterBranch.register_optimiser(InterToBranch5)