~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-02-03 10:06:19 UTC
  • mfrom: (4999.3.2 apport)
  • Revision ID: pqm@pqm.ubuntu.com-20100203100619-f76bo5y5bd5c14wk
(mbp) use apport to send bugs, not just store them

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007, 2008, 2009 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
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
from __future__ import absolute_import
18
 
 
19
 
import bzrlib.bzrdir
20
17
 
21
18
from cStringIO import StringIO
 
19
import sys
22
20
 
23
21
from bzrlib.lazy_import import lazy_import
24
22
lazy_import(globals(), """
25
 
import itertools
 
23
from itertools import chain
26
24
from bzrlib import (
27
 
    bzrdir,
28
 
    controldir,
29
 
    cache_utf8,
30
 
    cleanup,
31
 
    config as _mod_config,
32
 
    debug,
33
 
    errors,
34
 
    fetch,
35
 
    graph as _mod_graph,
36
 
    lockdir,
37
 
    lockable_files,
38
 
    remote,
39
 
    repository,
40
 
    revision as _mod_revision,
41
 
    rio,
42
 
    tag as _mod_tag,
43
 
    transport,
44
 
    ui,
45
 
    urlutils,
46
 
    vf_search,
 
25
        bzrdir,
 
26
        cache_utf8,
 
27
        config as _mod_config,
 
28
        debug,
 
29
        errors,
 
30
        lockdir,
 
31
        lockable_files,
 
32
        repository,
 
33
        revision as _mod_revision,
 
34
        rio,
 
35
        symbol_versioning,
 
36
        transport,
 
37
        tsort,
 
38
        ui,
 
39
        urlutils,
 
40
        )
 
41
from bzrlib.config import BranchConfig, TransportConfig
 
42
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
 
43
from bzrlib.tag import (
 
44
    BasicTags,
 
45
    DisabledTags,
47
46
    )
48
 
from bzrlib.i18n import gettext, ngettext
49
47
""")
50
48
 
51
 
# Explicitly import bzrlib.bzrdir so that the BzrProber
52
 
# is guaranteed to be registered.
53
 
import bzrlib.bzrdir
54
 
 
55
 
from bzrlib import (
56
 
    bzrdir,
57
 
    controldir,
58
 
    )
59
 
from bzrlib.decorators import (
60
 
    needs_read_lock,
61
 
    needs_write_lock,
62
 
    only_raises,
63
 
    )
64
 
from bzrlib.hooks import Hooks
 
49
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
 
50
from bzrlib.hooks import HookPoint, Hooks
65
51
from bzrlib.inter import InterObject
66
 
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
 
52
from bzrlib.lock import _RelockDebugMixin
67
53
from bzrlib import registry
68
54
from bzrlib.symbol_versioning import (
69
55
    deprecated_in,
72
58
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
73
59
 
74
60
 
75
 
class Branch(controldir.ControlComponent):
 
61
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
 
62
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
 
63
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
 
64
 
 
65
 
 
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):
76
79
    """Branch holding a history of revisions.
77
80
 
78
 
    :ivar base:
79
 
        Base directory/url of the branch; using control_url and
80
 
        control_transport is more standardized.
81
 
    :ivar hooks: An instance of BranchHooks.
82
 
    :ivar _master_branch_cache: cached result of get_master_branch, see
83
 
        _clear_cached_state.
 
81
    base
 
82
        Base directory/url of the branch.
 
83
 
 
84
    hooks: An instance of BranchHooks.
84
85
    """
85
86
    # this is really an instance variable - FIXME move it there
86
87
    # - RBC 20060112
87
88
    base = None
88
89
 
89
 
    @property
90
 
    def control_transport(self):
91
 
        return self._transport
92
 
 
93
 
    @property
94
 
    def user_transport(self):
95
 
        return self.bzrdir.user_transport
96
 
 
97
 
    def __init__(self, possible_transports=None):
 
90
    def __init__(self, *ignored, **ignored_too):
98
91
        self.tags = self._format.make_tags(self)
99
92
        self._revision_history_cache = None
100
93
        self._revision_id_to_revno_cache = None
101
94
        self._partial_revision_id_to_revno_cache = {}
102
95
        self._partial_revision_history_cache = []
103
 
        self._tags_bytes = None
104
96
        self._last_revision_info_cache = None
105
 
        self._master_branch_cache = None
106
97
        self._merge_sorted_revisions_cache = None
107
 
        self._open_hook(possible_transports)
 
98
        self._open_hook()
108
99
        hooks = Branch.hooks['open']
109
100
        for hook in hooks:
110
101
            hook(self)
111
102
 
112
 
    def _open_hook(self, possible_transports):
 
103
    def _open_hook(self):
113
104
        """Called by init to allow simpler extension of the base class."""
114
105
 
115
 
    def _activate_fallback_location(self, url, possible_transports):
 
106
    def _activate_fallback_location(self, url):
116
107
        """Activate the branch/repository from url as a fallback repository."""
117
 
        for existing_fallback_repo in self.repository._fallback_repositories:
118
 
            if existing_fallback_repo.user_url == url:
119
 
                # This fallback is already configured.  This probably only
120
 
                # happens because ControlDir.sprout is a horrible mess.  To avoid
121
 
                # confusing _unstack we don't add this a second time.
122
 
                mutter('duplicate activation of fallback %r on %r', url, self)
123
 
                return
124
 
        repo = self._get_fallback_repository(url, possible_transports)
 
108
        repo = self._get_fallback_repository(url)
125
109
        if repo.has_same_location(self.repository):
126
 
            raise errors.UnstackableLocationError(self.user_url, url)
 
110
            raise errors.UnstackableLocationError(self.base, url)
127
111
        self.repository.add_fallback_repository(repo)
128
112
 
129
113
    def break_lock(self):
181
165
        For instance, if the branch is at URL/.bzr/branch,
182
166
        Branch.open(URL) -> a Branch instance.
183
167
        """
184
 
        control = controldir.ControlDir.open(base,
185
 
            possible_transports=possible_transports, _unsupported=_unsupported)
186
 
        return control.open_branch(unsupported=_unsupported,
187
 
            possible_transports=possible_transports)
 
168
        control = bzrdir.BzrDir.open(base, _unsupported,
 
169
                                     possible_transports=possible_transports)
 
170
        return control.open_branch(_unsupported)
188
171
 
189
172
    @staticmethod
190
 
    def open_from_transport(transport, name=None, _unsupported=False,
191
 
            possible_transports=None):
 
173
    def open_from_transport(transport, _unsupported=False):
192
174
        """Open the branch rooted at transport"""
193
 
        control = controldir.ControlDir.open_from_transport(transport, _unsupported)
194
 
        return control.open_branch(name=name, unsupported=_unsupported,
195
 
            possible_transports=possible_transports)
 
175
        control = bzrdir.BzrDir.open_from_transport(transport, _unsupported)
 
176
        return control.open_branch(_unsupported)
196
177
 
197
178
    @staticmethod
198
179
    def open_containing(url, possible_transports=None):
206
187
        format, UnknownFormatError or UnsupportedFormatError are raised.
207
188
        If there is one, it is returned, along with the unused portion of url.
208
189
        """
209
 
        control, relpath = controldir.ControlDir.open_containing(url,
 
190
        control, relpath = bzrdir.BzrDir.open_containing(url,
210
191
                                                         possible_transports)
211
 
        branch = control.open_branch(possible_transports=possible_transports)
212
 
        return (branch, relpath)
 
192
        return control.open_branch(), relpath
213
193
 
214
194
    def _push_should_merge_tags(self):
215
195
        """Should _basic_push merge this branch's tags into the target?
220
200
        return self.supports_tags() and self.tags.get_tag_dict()
221
201
 
222
202
    def get_config(self):
223
 
        """Get a bzrlib.config.BranchConfig for this Branch.
224
 
 
225
 
        This can then be used to get and set configuration options for the
226
 
        branch.
227
 
 
228
 
        :return: A bzrlib.config.BranchConfig.
229
 
        """
230
 
        return _mod_config.BranchConfig(self)
231
 
 
232
 
    def get_config_stack(self):
233
 
        """Get a bzrlib.config.BranchStack for this Branch.
234
 
 
235
 
        This can then be used to get and set configuration options for the
236
 
        branch.
237
 
 
238
 
        :return: A bzrlib.config.BranchStack.
239
 
        """
240
 
        return _mod_config.BranchStack(self)
 
203
        return BranchConfig(self)
241
204
 
242
205
    def _get_config(self):
243
206
        """Get the concrete config for just the config in this branch.
251
214
        """
252
215
        raise NotImplementedError(self._get_config)
253
216
 
254
 
    def _get_fallback_repository(self, url, possible_transports):
 
217
    def _get_fallback_repository(self, url):
255
218
        """Get the repository we fallback to at url."""
256
219
        url = urlutils.join(self.base, url)
257
 
        a_branch = Branch.open(url, possible_transports=possible_transports)
258
 
        return a_branch.repository
 
220
        a_bzrdir = bzrdir.BzrDir.open(url,
 
221
            possible_transports=[self.bzrdir.root_transport])
 
222
        return a_bzrdir.open_branch().repository
259
223
 
260
 
    @needs_read_lock
261
224
    def _get_tags_bytes(self):
262
225
        """Get the bytes of a serialised tags dict.
263
226
 
270
233
        :return: The bytes of the tags file.
271
234
        :seealso: Branch._set_tags_bytes.
272
235
        """
273
 
        if self._tags_bytes is None:
274
 
            self._tags_bytes = self._transport.get_bytes('tags')
275
 
        return self._tags_bytes
 
236
        return self._transport.get_bytes('tags')
276
237
 
277
238
    def _get_nick(self, local=False, possible_transports=None):
278
239
        config = self.get_config()
280
241
        if not local and not config.has_explicit_nickname():
281
242
            try:
282
243
                master = self.get_master_branch(possible_transports)
283
 
                if master and self.user_url == master.user_url:
284
 
                    raise errors.RecursiveBind(self.user_url)
285
244
                if master is not None:
286
245
                    # return the master branch value
287
246
                    return master.nick
288
 
            except errors.RecursiveBind, e:
289
 
                raise e
290
247
            except errors.BzrError, e:
291
248
                # Silently fall back to local implicit nick if the master is
292
249
                # unavailable
329
286
        new_history.reverse()
330
287
        return new_history
331
288
 
332
 
    def lock_write(self, token=None):
333
 
        """Lock the branch for write operations.
334
 
 
335
 
        :param token: A token to permit reacquiring a previously held and
336
 
            preserved lock.
337
 
        :return: A BranchWriteLockResult.
338
 
        """
 
289
    def lock_write(self):
339
290
        raise NotImplementedError(self.lock_write)
340
291
 
341
292
    def lock_read(self):
342
 
        """Lock the branch for read operations.
343
 
 
344
 
        :return: A bzrlib.lock.LogicalLockResult.
345
 
        """
346
293
        raise NotImplementedError(self.lock_read)
347
294
 
348
295
    def unlock(self):
469
416
            after. If None, the rest of history is included.
470
417
        :param stop_rule: if stop_revision_id is not None, the precise rule
471
418
            to use for termination:
472
 
 
473
419
            * 'exclude' - leave the stop revision out of the result (default)
474
420
            * 'include' - the stop revision is the last item in the result
475
421
            * 'with-merges' - include the stop revision and all of its
476
422
              merged revisions in the result
477
 
            * 'with-merges-without-common-ancestry' - filter out revisions 
478
 
              that are in both ancestries
479
423
        :param direction: either 'reverse' or 'forward':
480
 
 
481
424
            * reverse means return the start_revision_id first, i.e.
482
425
              start at the most recent revision and go backwards in history
483
426
            * forward returns tuples in the opposite order to reverse.
504
447
        # start_revision_id.
505
448
        if self._merge_sorted_revisions_cache is None:
506
449
            last_revision = self.last_revision()
507
 
            known_graph = self.repository.get_known_graph_ancestry(
508
 
                [last_revision])
 
450
            last_key = (last_revision,)
 
451
            known_graph = self.repository.revisions.get_known_graph_ancestry(
 
452
                [last_key])
509
453
            self._merge_sorted_revisions_cache = known_graph.merge_sort(
510
 
                last_revision)
 
454
                last_key)
511
455
        filtered = self._filter_merge_sorted_revisions(
512
456
            self._merge_sorted_revisions_cache, start_revision_id,
513
457
            stop_revision_id, stop_rule)
514
 
        # Make sure we don't return revisions that are not part of the
515
 
        # start_revision_id ancestry.
516
 
        filtered = self._filter_start_non_ancestors(filtered)
517
458
        if direction == 'reverse':
518
459
            return filtered
519
460
        if direction == 'forward':
527
468
        rev_iter = iter(merge_sorted_revisions)
528
469
        if start_revision_id is not None:
529
470
            for node in rev_iter:
530
 
                rev_id = node.key
 
471
                rev_id = node.key[-1]
531
472
                if rev_id != start_revision_id:
532
473
                    continue
533
474
                else:
534
475
                    # The decision to include the start or not
535
476
                    # depends on the stop_rule if a stop is provided
536
477
                    # so pop this node back into the iterator
537
 
                    rev_iter = itertools.chain(iter([node]), rev_iter)
 
478
                    rev_iter = chain(iter([node]), rev_iter)
538
479
                    break
539
480
        if stop_revision_id is None:
540
481
            # Yield everything
541
482
            for node in rev_iter:
542
 
                rev_id = node.key
 
483
                rev_id = node.key[-1]
543
484
                yield (rev_id, node.merge_depth, node.revno,
544
485
                       node.end_of_merge)
545
486
        elif stop_rule == 'exclude':
546
487
            for node in rev_iter:
547
 
                rev_id = node.key
 
488
                rev_id = node.key[-1]
548
489
                if rev_id == stop_revision_id:
549
490
                    return
550
491
                yield (rev_id, node.merge_depth, node.revno,
551
492
                       node.end_of_merge)
552
493
        elif stop_rule == 'include':
553
494
            for node in rev_iter:
554
 
                rev_id = node.key
 
495
                rev_id = node.key[-1]
555
496
                yield (rev_id, node.merge_depth, node.revno,
556
497
                       node.end_of_merge)
557
498
                if rev_id == stop_revision_id:
558
499
                    return
559
 
        elif stop_rule == 'with-merges-without-common-ancestry':
560
 
            # We want to exclude all revisions that are already part of the
561
 
            # stop_revision_id ancestry.
562
 
            graph = self.repository.get_graph()
563
 
            ancestors = graph.find_unique_ancestors(start_revision_id,
564
 
                                                    [stop_revision_id])
565
 
            for node in rev_iter:
566
 
                rev_id = node.key
567
 
                if rev_id not in ancestors:
568
 
                    continue
569
 
                yield (rev_id, node.merge_depth, node.revno,
570
 
                       node.end_of_merge)
571
500
        elif stop_rule == 'with-merges':
572
501
            stop_rev = self.repository.get_revision(stop_revision_id)
573
502
            if stop_rev.parent_ids:
579
508
            reached_stop_revision_id = False
580
509
            revision_id_whitelist = []
581
510
            for node in rev_iter:
582
 
                rev_id = node.key
 
511
                rev_id = node.key[-1]
583
512
                if rev_id == left_parent:
584
513
                    # reached the left parent after the stop_revision
585
514
                    return
596
525
        else:
597
526
            raise ValueError('invalid stop_rule %r' % stop_rule)
598
527
 
599
 
    def _filter_start_non_ancestors(self, rev_iter):
600
 
        # If we started from a dotted revno, we want to consider it as a tip
601
 
        # and don't want to yield revisions that are not part of its
602
 
        # ancestry. Given the order guaranteed by the merge sort, we will see
603
 
        # uninteresting descendants of the first parent of our tip before the
604
 
        # tip itself.
605
 
        first = rev_iter.next()
606
 
        (rev_id, merge_depth, revno, end_of_merge) = first
607
 
        yield first
608
 
        if not merge_depth:
609
 
            # We start at a mainline revision so by definition, all others
610
 
            # revisions in rev_iter are ancestors
611
 
            for node in rev_iter:
612
 
                yield node
613
 
 
614
 
        clean = False
615
 
        whitelist = set()
616
 
        pmap = self.repository.get_parent_map([rev_id])
617
 
        parents = pmap.get(rev_id, [])
618
 
        if parents:
619
 
            whitelist.update(parents)
620
 
        else:
621
 
            # If there is no parents, there is nothing of interest left
622
 
 
623
 
            # FIXME: It's hard to test this scenario here as this code is never
624
 
            # called in that case. -- vila 20100322
625
 
            return
626
 
 
627
 
        for (rev_id, merge_depth, revno, end_of_merge) in rev_iter:
628
 
            if not clean:
629
 
                if rev_id in whitelist:
630
 
                    pmap = self.repository.get_parent_map([rev_id])
631
 
                    parents = pmap.get(rev_id, [])
632
 
                    whitelist.remove(rev_id)
633
 
                    whitelist.update(parents)
634
 
                    if merge_depth == 0:
635
 
                        # We've reached the mainline, there is nothing left to
636
 
                        # filter
637
 
                        clean = True
638
 
                else:
639
 
                    # A revision that is not part of the ancestry of our
640
 
                    # starting revision.
641
 
                    continue
642
 
            yield (rev_id, merge_depth, revno, end_of_merge)
643
 
 
644
528
    def leave_lock_in_place(self):
645
529
        """Tell this branch object not to release the physical lock when this
646
530
        object is unlocked.
663
547
        :param other: The branch to bind to
664
548
        :type other: Branch
665
549
        """
666
 
        raise errors.UpgradeRequired(self.user_url)
667
 
 
668
 
    def get_append_revisions_only(self):
669
 
        """Whether it is only possible to append revisions to the history.
670
 
        """
671
 
        if not self._format.supports_set_append_revisions_only():
672
 
            return False
673
 
        return self.get_config_stack().get('append_revisions_only')
 
550
        raise errors.UpgradeRequired(self.base)
674
551
 
675
552
    def set_append_revisions_only(self, enabled):
676
553
        if not self._format.supports_set_append_revisions_only():
677
 
            raise errors.UpgradeRequired(self.user_url)
678
 
        self.get_config_stack().set('append_revisions_only', enabled)
 
554
            raise errors.UpgradeRequired(self.base)
 
555
        if enabled:
 
556
            value = 'True'
 
557
        else:
 
558
            value = 'False'
 
559
        self.get_config().set_user_option('append_revisions_only', value,
 
560
            warn_masked=True)
679
561
 
680
562
    def set_reference_info(self, file_id, tree_path, branch_location):
681
563
        """Set the branch location to use for a tree reference."""
686
568
        raise errors.UnsupportedOperation(self.get_reference_info, self)
687
569
 
688
570
    @needs_write_lock
689
 
    def fetch(self, from_branch, last_revision=None, limit=None):
 
571
    def fetch(self, from_branch, last_revision=None, pb=None):
690
572
        """Copy revisions from from_branch into this branch.
691
573
 
692
574
        :param from_branch: Where to copy from.
693
575
        :param last_revision: What revision to stop at (None for at the end
694
576
                              of the branch.
695
 
        :param limit: Optional rough limit of revisions to fetch
 
577
        :param pb: An optional progress bar to use.
696
578
        :return: None
697
579
        """
698
 
        return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
 
580
        if self.base == from_branch.base:
 
581
            return (0, [])
 
582
        if pb is not None:
 
583
            symbol_versioning.warn(
 
584
                symbol_versioning.deprecated_in((1, 14, 0))
 
585
                % "pb parameter to fetch()")
 
586
        from_branch.lock_read()
 
587
        try:
 
588
            if last_revision is None:
 
589
                last_revision = from_branch.last_revision()
 
590
                last_revision = _mod_revision.ensure_null(last_revision)
 
591
            return self.repository.fetch(from_branch.repository,
 
592
                                         revision_id=last_revision,
 
593
                                         pb=pb)
 
594
        finally:
 
595
            from_branch.unlock()
699
596
 
700
597
    def get_bound_location(self):
701
598
        """Return the URL of the branch we are bound to.
708
605
    def get_old_bound_location(self):
709
606
        """Return the URL of the branch we used to be bound to
710
607
        """
711
 
        raise errors.UpgradeRequired(self.user_url)
 
608
        raise errors.UpgradeRequired(self.base)
712
609
 
713
 
    def get_commit_builder(self, parents, config_stack=None, timestamp=None,
 
610
    def get_commit_builder(self, parents, config=None, timestamp=None,
714
611
                           timezone=None, committer=None, revprops=None,
715
 
                           revision_id=None, lossy=False):
 
612
                           revision_id=None):
716
613
        """Obtain a CommitBuilder for this branch.
717
614
 
718
615
        :param parents: Revision ids of the parents of the new revision.
722
619
        :param committer: Optional committer to set for commit.
723
620
        :param revprops: Optional dictionary of revision properties.
724
621
        :param revision_id: Optional revision id.
725
 
        :param lossy: Whether to discard data that can not be natively
726
 
            represented, when pushing to a foreign VCS 
727
622
        """
728
623
 
729
 
        if config_stack is None:
730
 
            config_stack = self.get_config_stack()
 
624
        if config is None:
 
625
            config = self.get_config()
731
626
 
732
 
        return self.repository.get_commit_builder(self, parents, config_stack,
733
 
            timestamp, timezone, committer, revprops, revision_id,
734
 
            lossy)
 
627
        return self.repository.get_commit_builder(self, parents, config,
 
628
            timestamp, timezone, committer, revprops, revision_id)
735
629
 
736
630
    def get_master_branch(self, possible_transports=None):
737
631
        """Return the branch we are bound to.
740
634
        """
741
635
        return None
742
636
 
743
 
    @deprecated_method(deprecated_in((2, 5, 0)))
744
637
    def get_revision_delta(self, revno):
745
638
        """Return the delta for one revision.
746
639
 
747
640
        The delta is relative to its mainline predecessor, or the
748
641
        empty tree for revision 1.
749
642
        """
750
 
        try:
751
 
            revid = self.get_rev_id(revno)
752
 
        except errors.NoSuchRevision:
 
643
        rh = self.revision_history()
 
644
        if not (1 <= revno <= len(rh)):
753
645
            raise errors.InvalidRevisionNumber(revno)
754
 
        return self.repository.get_revision_delta(revid)
 
646
        return self.repository.get_revision_delta(rh[revno-1])
755
647
 
756
648
    def get_stacked_on_url(self):
757
649
        """Get the URL this branch is stacked against.
766
658
        """Print `file` to stdout."""
767
659
        raise NotImplementedError(self.print_file)
768
660
 
769
 
    @deprecated_method(deprecated_in((2, 4, 0)))
770
661
    def set_revision_history(self, rev_history):
771
 
        """See Branch.set_revision_history."""
772
 
        self._set_revision_history(rev_history)
773
 
 
774
 
    @needs_write_lock
775
 
    def _set_revision_history(self, rev_history):
776
 
        if len(rev_history) == 0:
777
 
            revid = _mod_revision.NULL_REVISION
778
 
        else:
779
 
            revid = rev_history[-1]
780
 
        if rev_history != self._lefthand_history(revid):
781
 
            raise errors.NotLefthandHistory(rev_history)
782
 
        self.set_last_revision_info(len(rev_history), revid)
783
 
        self._cache_revision_history(rev_history)
784
 
        for hook in Branch.hooks['set_rh']:
785
 
            hook(self, rev_history)
786
 
 
787
 
    @needs_write_lock
788
 
    def set_last_revision_info(self, revno, revision_id):
789
 
        """Set the last revision of this branch.
790
 
 
791
 
        The caller is responsible for checking that the revno is correct
792
 
        for this revision id.
793
 
 
794
 
        It may be possible to set the branch last revision to an id not
795
 
        present in the repository.  However, branches can also be
796
 
        configured to check constraints on history, in which case this may not
797
 
        be permitted.
798
 
        """
799
 
        raise NotImplementedError(self.set_last_revision_info)
800
 
 
801
 
    @needs_write_lock
802
 
    def generate_revision_history(self, revision_id, last_rev=None,
803
 
                                  other_branch=None):
804
 
        """See Branch.generate_revision_history"""
805
 
        graph = self.repository.get_graph()
806
 
        (last_revno, last_revid) = self.last_revision_info()
807
 
        known_revision_ids = [
808
 
            (last_revid, last_revno),
809
 
            (_mod_revision.NULL_REVISION, 0),
810
 
            ]
811
 
        if last_rev is not None:
812
 
            if not graph.is_ancestor(last_rev, revision_id):
813
 
                # our previous tip is not merged into stop_revision
814
 
                raise errors.DivergedBranches(self, other_branch)
815
 
        revno = graph.find_distance_to_null(revision_id, known_revision_ids)
816
 
        self.set_last_revision_info(revno, revision_id)
 
662
        raise NotImplementedError(self.set_revision_history)
817
663
 
818
664
    @needs_write_lock
819
665
    def set_parent(self, url):
843
689
            stacking.
844
690
        """
845
691
        if not self._format.supports_stacking():
846
 
            raise errors.UnstackableBranchFormat(self._format, self.user_url)
 
692
            raise errors.UnstackableBranchFormat(self._format, self.base)
847
693
        # XXX: Changing from one fallback repository to another does not check
848
694
        # that all the data you need is present in the new fallback.
849
695
        # Possibly it should.
856
702
                return
857
703
            self._unstack()
858
704
        else:
859
 
            self._activate_fallback_location(url,
860
 
                possible_transports=[self.bzrdir.root_transport])
 
705
            self._activate_fallback_location(url)
861
706
        # write this out after the repository is stacked to avoid setting a
862
707
        # stacked config that doesn't work.
863
708
        self._set_config_location('stacked_on_location', url)
864
709
 
865
710
    def _unstack(self):
866
711
        """Change a branch to be unstacked, copying data as needed.
867
 
 
 
712
        
868
713
        Don't call this directly, use set_stacked_on_url(None).
869
714
        """
870
715
        pb = ui.ui_factory.nested_progress_bar()
871
716
        try:
872
 
            pb.update(gettext("Unstacking"))
 
717
            pb.update("Unstacking")
873
718
            # The basic approach here is to fetch the tip of the branch,
874
719
            # including all available ghosts, from the existing stacked
875
720
            # repository into a new repository object without the fallbacks. 
879
724
            old_repository = self.repository
880
725
            if len(old_repository._fallback_repositories) != 1:
881
726
                raise AssertionError("can't cope with fallback repositories "
882
 
                    "of %r (fallbacks: %r)" % (old_repository,
883
 
                        old_repository._fallback_repositories))
884
 
            # Open the new repository object.
885
 
            # Repositories don't offer an interface to remove fallback
886
 
            # repositories today; take the conceptually simpler option and just
887
 
            # reopen it.  We reopen it starting from the URL so that we
888
 
            # get a separate connection for RemoteRepositories and can
889
 
            # stream from one of them to the other.  This does mean doing
890
 
            # separate SSH connection setup, but unstacking is not a
891
 
            # common operation so it's tolerable.
892
 
            new_bzrdir = controldir.ControlDir.open(
893
 
                self.bzrdir.root_transport.base)
894
 
            new_repository = new_bzrdir.find_repository()
895
 
            if new_repository._fallback_repositories:
896
 
                raise AssertionError("didn't expect %r to have "
897
 
                    "fallback_repositories"
898
 
                    % (self.repository,))
899
 
            # Replace self.repository with the new repository.
900
 
            # Do our best to transfer the lock state (i.e. lock-tokens and
901
 
            # lock count) of self.repository to the new repository.
902
 
            lock_token = old_repository.lock_write().repository_token
903
 
            self.repository = new_repository
904
 
            if isinstance(self, remote.RemoteBranch):
905
 
                # Remote branches can have a second reference to the old
906
 
                # repository that need to be replaced.
907
 
                if self._real_branch is not None:
908
 
                    self._real_branch.repository = new_repository
909
 
            self.repository.lock_write(token=lock_token)
910
 
            if lock_token is not None:
911
 
                old_repository.leave_lock_in_place()
 
727
                    "of %r" % (self.repository,))
 
728
            # unlock it, including unlocking the fallback
912
729
            old_repository.unlock()
913
 
            if lock_token is not None:
914
 
                # XXX: self.repository.leave_lock_in_place() before this
915
 
                # function will not be preserved.  Fortunately that doesn't
916
 
                # affect the current default format (2a), and would be a
917
 
                # corner-case anyway.
918
 
                #  - Andrew Bennetts, 2010/06/30
919
 
                self.repository.dont_leave_lock_in_place()
920
 
            old_lock_count = 0
921
 
            while True:
922
 
                try:
923
 
                    old_repository.unlock()
924
 
                except errors.LockNotHeld:
925
 
                    break
926
 
                old_lock_count += 1
927
 
            if old_lock_count == 0:
928
 
                raise AssertionError(
929
 
                    'old_repository should have been locked at least once.')
930
 
            for i in range(old_lock_count-1):
 
730
            old_repository.lock_read()
 
731
            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
931
749
                self.repository.lock_write()
932
 
            # Fetch from the old repository into the new.
933
 
            old_repository.lock_read()
934
 
            try:
935
750
                # XXX: If you unstack a branch while it has a working tree
936
751
                # with a pending merge, the pending-merged revisions will no
937
752
                # longer be present.  You can (probably) revert and remerge.
938
 
                try:
939
 
                    tags_to_fetch = set(self.tags.get_reverse_tag_dict())
940
 
                except errors.TagsNotSupported:
941
 
                    tags_to_fetch = set()
942
 
                fetch_spec = vf_search.NotInOtherForRevs(self.repository,
943
 
                    old_repository, required_ids=[self.last_revision()],
944
 
                    if_present_ids=tags_to_fetch, find_ghosts=True).execute()
945
 
                self.repository.fetch(old_repository, fetch_spec=fetch_spec)
 
753
                #
 
754
                # XXX: This only fetches up to the tip of the repository; it
 
755
                # doesn't bring across any tags.  That's fairly consistent
 
756
                # with how branch works, but perhaps not ideal.
 
757
                self.repository.fetch(old_repository,
 
758
                    revision_id=self.last_revision(),
 
759
                    find_ghosts=True)
946
760
            finally:
947
761
                old_repository.unlock()
948
762
        finally:
953
767
 
954
768
        :seealso: Branch._get_tags_bytes.
955
769
        """
956
 
        op = cleanup.OperationWithCleanups(self._set_tags_bytes_locked)
957
 
        op.add_cleanup(self.lock_write().unlock)
958
 
        return op.run_simple(bytes)
959
 
 
960
 
    def _set_tags_bytes_locked(self, bytes):
961
 
        self._tags_bytes = bytes
962
 
        return self._transport.put_bytes('tags', bytes)
 
770
        return _run_with_write_locked_target(self, self._transport.put_bytes,
 
771
            'tags', bytes)
963
772
 
964
773
    def _cache_revision_history(self, rev_history):
965
774
        """Set the cached revision history to rev_history.
992
801
        self._revision_history_cache = None
993
802
        self._revision_id_to_revno_cache = None
994
803
        self._last_revision_info_cache = None
995
 
        self._master_branch_cache = None
996
804
        self._merge_sorted_revisions_cache = None
997
805
        self._partial_revision_history_cache = []
998
806
        self._partial_revision_id_to_revno_cache = {}
999
 
        self._tags_bytes = None
1000
807
 
1001
808
    def _gen_revision_history(self):
1002
809
        """Return sequence of revision hashes on to this branch.
1013
820
        """
1014
821
        raise NotImplementedError(self._gen_revision_history)
1015
822
 
1016
 
    @deprecated_method(deprecated_in((2, 5, 0)))
1017
823
    @needs_read_lock
1018
824
    def revision_history(self):
1019
825
        """Return sequence of revision ids on this branch.
1021
827
        This method will cache the revision history for as long as it is safe to
1022
828
        do so.
1023
829
        """
1024
 
        return self._revision_history()
1025
 
 
1026
 
    def _revision_history(self):
1027
830
        if 'evil' in debug.debug_flags:
1028
831
            mutter_callsite(3, "revision_history scales with history.")
1029
832
        if self._revision_history_cache is not None:
1043
846
 
1044
847
    def unbind(self):
1045
848
        """Older format branches cannot bind or unbind."""
1046
 
        raise errors.UpgradeRequired(self.user_url)
 
849
        raise errors.UpgradeRequired(self.base)
1047
850
 
1048
851
    def last_revision(self):
1049
852
        """Return last revision id, or NULL_REVISION."""
1056
859
        :return: A tuple (revno, revision_id).
1057
860
        """
1058
861
        if self._last_revision_info_cache is None:
1059
 
            self._last_revision_info_cache = self._read_last_revision_info()
 
862
            self._last_revision_info_cache = self._last_revision_info()
1060
863
        return self._last_revision_info_cache
1061
864
 
1062
 
    def _read_last_revision_info(self):
1063
 
        raise NotImplementedError(self._read_last_revision_info)
1064
 
 
1065
 
    @deprecated_method(deprecated_in((2, 4, 0)))
 
865
    def _last_revision_info(self):
 
866
        rh = self.revision_history()
 
867
        revno = len(rh)
 
868
        if revno:
 
869
            return (revno, rh[-1])
 
870
        else:
 
871
            return (0, _mod_revision.NULL_REVISION)
 
872
 
 
873
    @deprecated_method(deprecated_in((1, 6, 0)))
 
874
    def missing_revisions(self, other, stop_revision=None):
 
875
        """Return a list of new revisions that would perfectly fit.
 
876
 
 
877
        If self and other have not diverged, return a list of the revisions
 
878
        present in other, but missing from self.
 
879
        """
 
880
        self_history = self.revision_history()
 
881
        self_len = len(self_history)
 
882
        other_history = other.revision_history()
 
883
        other_len = len(other_history)
 
884
        common_index = min(self_len, other_len) -1
 
885
        if common_index >= 0 and \
 
886
            self_history[common_index] != other_history[common_index]:
 
887
            raise errors.DivergedBranches(self, other)
 
888
 
 
889
        if stop_revision is None:
 
890
            stop_revision = other_len
 
891
        else:
 
892
            if stop_revision > other_len:
 
893
                raise errors.NoSuchRevision(self, stop_revision)
 
894
        return other_history[self_len:stop_revision]
 
895
 
 
896
    @needs_write_lock
 
897
    def update_revisions(self, other, stop_revision=None, overwrite=False,
 
898
                         graph=None):
 
899
        """Pull in new perfect-fit revisions.
 
900
 
 
901
        :param other: Another Branch to pull from
 
902
        :param stop_revision: Updated until the given revision
 
903
        :param overwrite: Always set the branch pointer, rather than checking
 
904
            to see if it is a proper descendant.
 
905
        :param graph: A Graph object that can be used to query history
 
906
            information. This can be None.
 
907
        :return: None
 
908
        """
 
909
        return InterBranch.get(other, self).update_revisions(stop_revision,
 
910
            overwrite, graph)
 
911
 
1066
912
    def import_last_revision_info(self, source_repo, revno, revid):
1067
913
        """Set the last revision info, importing from another repo if necessary.
1068
914
 
 
915
        This is used by the bound branch code to upload a revision to
 
916
        the master branch first before updating the tip of the local branch.
 
917
 
1069
918
        :param source_repo: Source repository to optionally fetch from
1070
919
        :param revno: Revision number of the new tip
1071
920
        :param revid: Revision id of the new tip
1074
923
            self.repository.fetch(source_repo, revision_id=revid)
1075
924
        self.set_last_revision_info(revno, revid)
1076
925
 
1077
 
    def import_last_revision_info_and_tags(self, source, revno, revid,
1078
 
                                           lossy=False):
1079
 
        """Set the last revision info, importing from another repo if necessary.
1080
 
 
1081
 
        This is used by the bound branch code to upload a revision to
1082
 
        the master branch first before updating the tip of the local branch.
1083
 
        Revisions referenced by source's tags are also transferred.
1084
 
 
1085
 
        :param source: Source branch to optionally fetch from
1086
 
        :param revno: Revision number of the new tip
1087
 
        :param revid: Revision id of the new tip
1088
 
        :param lossy: Whether to discard metadata that can not be
1089
 
            natively represented
1090
 
        :return: Tuple with the new revision number and revision id
1091
 
            (should only be different from the arguments when lossy=True)
1092
 
        """
1093
 
        if not self.repository.has_same_location(source.repository):
1094
 
            self.fetch(source, revid)
1095
 
        self.set_last_revision_info(revno, revid)
1096
 
        return (revno, revid)
1097
 
 
1098
926
    def revision_id_to_revno(self, revision_id):
1099
927
        """Given a revision id, return its revno"""
1100
928
        if _mod_revision.is_null(revision_id):
1101
929
            return 0
1102
 
        history = self._revision_history()
 
930
        history = self.revision_history()
1103
931
        try:
1104
932
            return history.index(revision_id) + 1
1105
933
        except ValueError:
1120
948
            self._extend_partial_history(distance_from_last)
1121
949
        return self._partial_revision_history_cache[distance_from_last]
1122
950
 
 
951
    @needs_write_lock
1123
952
    def pull(self, source, overwrite=False, stop_revision=None,
1124
953
             possible_transports=None, *args, **kwargs):
1125
954
        """Mirror source into this branch.
1132
961
            stop_revision=stop_revision,
1133
962
            possible_transports=possible_transports, *args, **kwargs)
1134
963
 
1135
 
    def push(self, target, overwrite=False, stop_revision=None, lossy=False,
1136
 
            *args, **kwargs):
 
964
    def push(self, target, overwrite=False, stop_revision=None, *args,
 
965
        **kwargs):
1137
966
        """Mirror this branch into target.
1138
967
 
1139
968
        This branch is considered to be 'local', having low latency.
1140
969
        """
1141
970
        return InterBranch.get(self, target).push(overwrite, stop_revision,
1142
 
            lossy, *args, **kwargs)
 
971
            *args, **kwargs)
 
972
 
 
973
    def lossy_push(self, target, stop_revision=None):
 
974
        """Push deltas into another branch.
 
975
 
 
976
        :note: This does not, like push, retain the revision ids from 
 
977
            the source branch and will, rather than adding bzr-specific 
 
978
            metadata, push only those semantics of the revision that can be 
 
979
            natively represented by this branch' VCS.
 
980
 
 
981
        :param target: Target branch
 
982
        :param stop_revision: Revision to push, defaults to last revision.
 
983
        :return: BranchPushResult with an extra member revidmap: 
 
984
            A dictionary mapping revision ids from the target branch 
 
985
            to new revision ids in the target branch, for each 
 
986
            revision that was pushed.
 
987
        """
 
988
        inter = InterBranch.get(self, target)
 
989
        lossy_push = getattr(inter, "lossy_push", None)
 
990
        if lossy_push is None:
 
991
            raise errors.LossyPushToSameVCS(self, target)
 
992
        return lossy_push(stop_revision)
1143
993
 
1144
994
    def basis_tree(self):
1145
995
        """Return `Tree` object for last revision."""
1162
1012
        try:
1163
1013
            return urlutils.join(self.base[:-1], parent)
1164
1014
        except errors.InvalidURLJoin, e:
1165
 
            raise errors.InaccessibleParent(parent, self.user_url)
 
1015
            raise errors.InaccessibleParent(parent, self.base)
1166
1016
 
1167
1017
    def _get_parent_location(self):
1168
1018
        raise NotImplementedError(self._get_parent_location)
1170
1020
    def _set_config_location(self, name, url, config=None,
1171
1021
                             make_relative=False):
1172
1022
        if config is None:
1173
 
            config = self.get_config_stack()
 
1023
            config = self.get_config()
1174
1024
        if url is None:
1175
1025
            url = ''
1176
1026
        elif make_relative:
1177
1027
            url = urlutils.relative_url(self.base, url)
1178
 
        config.set(name, url)
 
1028
        config.set_user_option(name, url, warn_masked=True)
1179
1029
 
1180
1030
    def _get_config_location(self, name, config=None):
1181
1031
        if config is None:
1182
 
            config = self.get_config_stack()
1183
 
        location = config.get(name)
 
1032
            config = self.get_config()
 
1033
        location = config.get_user_option(name)
1184
1034
        if location == '':
1185
1035
            location = None
1186
1036
        return location
1187
1037
 
1188
1038
    def get_child_submit_format(self):
1189
1039
        """Return the preferred format of submissions to this branch."""
1190
 
        return self.get_config_stack().get('child_submit_format')
 
1040
        return self.get_config().get_user_option("child_submit_format")
1191
1041
 
1192
1042
    def get_submit_branch(self):
1193
1043
        """Return the submit location of the branch.
1196
1046
        pattern is that the user can override it by specifying a
1197
1047
        location.
1198
1048
        """
1199
 
        return self.get_config_stack().get('submit_branch')
 
1049
        return self.get_config().get_user_option('submit_branch')
1200
1050
 
1201
1051
    def set_submit_branch(self, location):
1202
1052
        """Return the submit location of the branch.
1205
1055
        pattern is that the user can override it by specifying a
1206
1056
        location.
1207
1057
        """
1208
 
        self.get_config_stack().set('submit_branch', location)
 
1058
        self.get_config().set_user_option('submit_branch', location,
 
1059
            warn_masked=True)
1209
1060
 
1210
1061
    def get_public_branch(self):
1211
1062
        """Return the public location of the branch.
1224
1075
        self._set_config_location('public_branch', location)
1225
1076
 
1226
1077
    def get_push_location(self):
1227
 
        """Return None or the location to push this branch to."""
1228
 
        return self.get_config_stack().get('push_location')
 
1078
        """Return the None or the location to push this branch to."""
 
1079
        push_loc = self.get_config().get_user_option('push_location')
 
1080
        return push_loc
1229
1081
 
1230
1082
    def set_push_location(self, location):
1231
1083
        """Set a new push location for this branch."""
1298
1150
        return result
1299
1151
 
1300
1152
    @needs_read_lock
1301
 
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None,
1302
 
            repository=None):
 
1153
    def sprout(self, to_bzrdir, revision_id=None, repository_policy=None):
1303
1154
        """Create a new line of development from the branch, into to_bzrdir.
1304
1155
 
1305
1156
        to_bzrdir controls the branch format.
1310
1161
        if (repository_policy is not None and
1311
1162
            repository_policy.requires_stacking()):
1312
1163
            to_bzrdir._format.require_stacking(_skip_repo=True)
1313
 
        result = to_bzrdir.create_branch(repository=repository)
 
1164
        result = to_bzrdir.create_branch()
1314
1165
        result.lock_write()
1315
1166
        try:
1316
1167
            if repository_policy is not None:
1317
1168
                repository_policy.configure_branch(result)
1318
1169
            self.copy_content_into(result, revision_id=revision_id)
1319
 
            master_url = self.get_bound_location()
1320
 
            if master_url is None:
1321
 
                result.set_parent(self.bzrdir.root_transport.base)
1322
 
            else:
1323
 
                result.set_parent(master_url)
 
1170
            result.set_parent(self.bzrdir.root_transport.base)
1324
1171
        finally:
1325
1172
            result.unlock()
1326
1173
        return result
1350
1197
                revno = 1
1351
1198
        destination.set_last_revision_info(revno, revision_id)
1352
1199
 
 
1200
    @needs_read_lock
1353
1201
    def copy_content_into(self, destination, revision_id=None):
1354
1202
        """Copy the content of self into destination.
1355
1203
 
1356
1204
        revision_id: if not None, the revision history in the new branch will
1357
1205
                     be truncated to end with revision_id.
1358
1206
        """
1359
 
        return InterBranch.get(self, destination).copy_content_into(
1360
 
            revision_id=revision_id)
 
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)
1361
1218
 
1362
1219
    def update_references(self, target):
1363
1220
        if not getattr(self._format, 'supports_reference_locations', False):
1400
1257
        # TODO: We should probably also check that self.revision_history
1401
1258
        # matches the repository for older branch formats.
1402
1259
        # If looking for the code that cross-checks repository parents against
1403
 
        # the Graph.iter_lefthand_ancestry output, that is now a repository
 
1260
        # the iter_reverse_revision_history output, that is now a repository
1404
1261
        # specific check.
1405
1262
        return result
1406
1263
 
1407
 
    def _get_checkout_format(self, lightweight=False):
 
1264
    def _get_checkout_format(self):
1408
1265
        """Return the most suitable metadir for a checkout of this branch.
1409
1266
        Weaves are used if this branch's repository uses weaves.
1410
1267
        """
1411
 
        format = self.repository.bzrdir.checkout_metadir()
1412
 
        format.set_branch_format(self._format)
 
1268
        if isinstance(self.bzrdir, bzrdir.BzrDirPreSplitOut):
 
1269
            from bzrlib.repofmt import weaverepo
 
1270
            format = bzrdir.BzrDirMetaFormat1()
 
1271
            format.repository_format = weaverepo.RepositoryFormat7()
 
1272
        else:
 
1273
            format = self.repository.bzrdir.checkout_metadir()
 
1274
            format.set_branch_format(self._format)
1413
1275
        return format
1414
1276
 
1415
1277
    def create_clone_on_transport(self, to_transport, revision_id=None,
1416
 
        stacked_on=None, create_prefix=False, use_existing_dir=False,
1417
 
        no_tree=None):
 
1278
        stacked_on=None, create_prefix=False, use_existing_dir=False):
1418
1279
        """Create a clone of this branch and its bzrdir.
1419
1280
 
1420
1281
        :param to_transport: The transport to clone onto.
1427
1288
        """
1428
1289
        # XXX: Fix the bzrdir API to allow getting the branch back from the
1429
1290
        # clone call. Or something. 20090224 RBC/spiv.
1430
 
        # XXX: Should this perhaps clone colocated branches as well, 
1431
 
        # rather than just the default branch? 20100319 JRV
1432
1291
        if revision_id is None:
1433
1292
            revision_id = self.last_revision()
1434
1293
        dir_to = self.bzrdir.clone_on_transport(to_transport,
1435
1294
            revision_id=revision_id, stacked_on=stacked_on,
1436
 
            create_prefix=create_prefix, use_existing_dir=use_existing_dir,
1437
 
            no_tree=no_tree)
 
1295
            create_prefix=create_prefix, use_existing_dir=use_existing_dir)
1438
1296
        return dir_to.open_branch()
1439
1297
 
1440
1298
    def create_checkout(self, to_location, revision_id=None,
1445
1303
        :param to_location: The url to produce the checkout at
1446
1304
        :param revision_id: The revision to check out
1447
1305
        :param lightweight: If True, produce a lightweight checkout, otherwise,
1448
 
            produce a bound branch (heavyweight checkout)
 
1306
        produce a bound branch (heavyweight checkout)
1449
1307
        :param accelerator_tree: A tree which can be used for retrieving file
1450
1308
            contents more quickly than the revision tree, i.e. a workingtree.
1451
1309
            The revision tree will be used for cases where accelerator_tree's
1456
1314
        """
1457
1315
        t = transport.get_transport(to_location)
1458
1316
        t.ensure_base()
1459
 
        format = self._get_checkout_format(lightweight=lightweight)
1460
 
        try:
 
1317
        if lightweight:
 
1318
            format = self._get_checkout_format()
1461
1319
            checkout = format.initialize_on_transport(t)
1462
 
        except errors.AlreadyControlDirError:
1463
 
            # It's fine if the control directory already exists,
1464
 
            # as long as there is no existing branch and working tree.
1465
 
            checkout = controldir.ControlDir.open_from_transport(t)
1466
 
            try:
1467
 
                checkout.open_branch()
1468
 
            except errors.NotBranchError:
1469
 
                pass
1470
 
            else:
1471
 
                raise errors.AlreadyControlDirError(t.base)
1472
 
            if checkout.control_transport.base == self.bzrdir.control_transport.base:
1473
 
                # When checking out to the same control directory,
1474
 
                # always create a lightweight checkout
1475
 
                lightweight = True
1476
 
 
1477
 
        if lightweight:
1478
 
            from_branch = checkout.set_branch_reference(target_branch=self)
 
1320
            from_branch = BranchReferenceFormat().initialize(checkout, self)
1479
1321
        else:
1480
 
            policy = checkout.determine_repository_policy()
1481
 
            repo = policy.acquire_repository()[0]
1482
 
            checkout_branch = checkout.create_branch()
 
1322
            format = self._get_checkout_format()
 
1323
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
 
1324
                to_location, force_new_tree=False, format=format)
 
1325
            checkout = checkout_branch.bzrdir
1483
1326
            checkout_branch.bind(self)
1484
1327
            # pull up to the specified revision_id to set the initial
1485
1328
            # branch tip correctly, and seed it with history.
1486
1329
            checkout_branch.pull(self, stop_revision=revision_id)
1487
 
            from_branch = None
 
1330
            from_branch=None
1488
1331
        tree = checkout.create_workingtree(revision_id,
1489
1332
                                           from_branch=from_branch,
1490
1333
                                           accelerator_tree=accelerator_tree,
1511
1354
 
1512
1355
    def reference_parent(self, file_id, path, possible_transports=None):
1513
1356
        """Return the parent branch for a tree-reference file_id
1514
 
 
1515
1357
        :param file_id: The file_id of the tree reference
1516
1358
        :param path: The path of the file_id in the tree
1517
1359
        :return: A branch associated with the file_id
1523
1365
    def supports_tags(self):
1524
1366
        return self._format.supports_tags()
1525
1367
 
1526
 
    def automatic_tag_name(self, revision_id):
1527
 
        """Try to automatically find the tag name for a revision.
1528
 
 
1529
 
        :param revision_id: Revision id of the revision.
1530
 
        :return: A tag name or None if no tag name could be determined.
1531
 
        """
1532
 
        for hook in Branch.hooks['automatic_tag_name']:
1533
 
            ret = hook(self, revision_id)
1534
 
            if ret is not None:
1535
 
                return ret
1536
 
        return None
1537
 
 
1538
1368
    def _check_if_descendant_or_diverged(self, revision_a, revision_b, graph,
1539
1369
                                         other_branch):
1540
1370
        """Ensure that revision_b is a descendant of revision_a.
1570
1400
        else:
1571
1401
            raise AssertionError("invalid heads: %r" % (heads,))
1572
1402
 
1573
 
    def heads_to_fetch(self):
1574
 
        """Return the heads that must and that should be fetched to copy this
1575
 
        branch into another repo.
1576
 
 
1577
 
        :returns: a 2-tuple of (must_fetch, if_present_fetch).  must_fetch is a
1578
 
            set of heads that must be fetched.  if_present_fetch is a set of
1579
 
            heads that must be fetched if present, but no error is necessary if
1580
 
            they are not present.
1581
 
        """
1582
 
        # For bzr native formats must_fetch is just the tip, and
1583
 
        # if_present_fetch are the tags.
1584
 
        must_fetch = set([self.last_revision()])
1585
 
        if_present_fetch = set()
1586
 
        if self.get_config_stack().get('branch.fetch_tags'):
1587
 
            try:
1588
 
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
1589
 
            except errors.TagsNotSupported:
1590
 
                pass
1591
 
        must_fetch.discard(_mod_revision.NULL_REVISION)
1592
 
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
1593
 
        return must_fetch, if_present_fetch
1594
 
 
1595
 
 
1596
 
class BranchFormat(controldir.ControlComponentFormat):
 
1403
 
 
1404
class BranchFormat(object):
1597
1405
    """An encapsulation of the initialization and open routines for a format.
1598
1406
 
1599
1407
    Formats provide three things:
1600
1408
     * An initialization routine,
1601
 
     * a format description
 
1409
     * a format string,
1602
1410
     * an open routine.
1603
1411
 
1604
1412
    Formats are placed in an dict by their format string for reference
1605
 
    during branch opening. It's not required that these be instances, they
 
1413
    during branch opening. Its not required that these be instances, they
1606
1414
    can be classes themselves with class methods - it simply depends on
1607
1415
    whether state is needed for a given format or not.
1608
1416
 
1611
1419
    object will be created every time regardless.
1612
1420
    """
1613
1421
 
 
1422
    _default_format = None
 
1423
    """The default format used for new branches."""
 
1424
 
 
1425
    _formats = {}
 
1426
    """The known formats."""
 
1427
 
 
1428
    can_set_append_revisions_only = True
 
1429
 
1614
1430
    def __eq__(self, other):
1615
1431
        return self.__class__ is other.__class__
1616
1432
 
1618
1434
        return not (self == other)
1619
1435
 
1620
1436
    @classmethod
1621
 
    @deprecated_method(deprecated_in((2, 4, 0)))
 
1437
    def find_format(klass, a_bzrdir):
 
1438
        """Return the format for the branch object in a_bzrdir."""
 
1439
        try:
 
1440
            transport = a_bzrdir.get_branch_transport(None)
 
1441
            format_string = transport.get_bytes("format")
 
1442
            return klass._formats[format_string]
 
1443
        except errors.NoSuchFile:
 
1444
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
 
1445
        except KeyError:
 
1446
            raise errors.UnknownFormatError(format=format_string, kind='branch')
 
1447
 
 
1448
    @classmethod
1622
1449
    def get_default_format(klass):
1623
1450
        """Return the current default format."""
1624
 
        return format_registry.get_default()
1625
 
 
1626
 
    @classmethod
1627
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1628
 
    def get_formats(klass):
1629
 
        """Get all the known formats.
1630
 
 
1631
 
        Warning: This triggers a load of all lazy registered formats: do not
1632
 
        use except when that is desireed.
1633
 
        """
1634
 
        return format_registry._get_all()
1635
 
 
1636
 
    def get_reference(self, controldir, name=None):
1637
 
        """Get the target reference of the branch in controldir.
 
1451
        return klass._default_format
 
1452
 
 
1453
    def get_reference(self, a_bzrdir):
 
1454
        """Get the target reference of the branch in a_bzrdir.
1638
1455
 
1639
1456
        format probing must have been completed before calling
1640
1457
        this method - it is assumed that the format of the branch
1641
 
        in controldir is correct.
 
1458
        in a_bzrdir is correct.
1642
1459
 
1643
 
        :param controldir: The controldir to get the branch data from.
1644
 
        :param name: Name of the colocated branch to fetch
 
1460
        :param a_bzrdir: The bzrdir to get the branch data from.
1645
1461
        :return: None if the branch is not a reference branch.
1646
1462
        """
1647
1463
        return None
1648
1464
 
1649
1465
    @classmethod
1650
 
    def set_reference(self, controldir, name, to_branch):
1651
 
        """Set the target reference of the branch in controldir.
 
1466
    def set_reference(self, a_bzrdir, to_branch):
 
1467
        """Set the target reference of the branch in a_bzrdir.
1652
1468
 
1653
1469
        format probing must have been completed before calling
1654
1470
        this method - it is assumed that the format of the branch
1655
 
        in controldir is correct.
 
1471
        in a_bzrdir is correct.
1656
1472
 
1657
 
        :param controldir: The controldir to set the branch reference for.
1658
 
        :param name: Name of colocated branch to set, None for default
 
1473
        :param a_bzrdir: The bzrdir to set the branch reference for.
1659
1474
        :param to_branch: branch that the checkout is to reference
1660
1475
        """
1661
1476
        raise NotImplementedError(self.set_reference)
1662
1477
 
 
1478
    def get_format_string(self):
 
1479
        """Return the ASCII format string that identifies this format."""
 
1480
        raise NotImplementedError(self.get_format_string)
 
1481
 
1663
1482
    def get_format_description(self):
1664
1483
        """Return the short format description for this format."""
1665
1484
        raise NotImplementedError(self.get_format_description)
1666
1485
 
1667
 
    def _run_post_branch_init_hooks(self, controldir, name, branch):
1668
 
        hooks = Branch.hooks['post_branch_init']
1669
 
        if not hooks:
1670
 
            return
1671
 
        params = BranchInitHookParams(self, controldir, name, branch)
1672
 
        for hook in hooks:
1673
 
            hook(params)
1674
 
 
1675
 
    def initialize(self, controldir, name=None, repository=None,
1676
 
                   append_revisions_only=None):
1677
 
        """Create a branch of this format in controldir.
1678
 
 
1679
 
        :param name: Name of the colocated branch to create.
 
1486
    def _initialize_helper(self, a_bzrdir, utf8_files, lock_type='metadir',
 
1487
                           set_format=True):
 
1488
        """Initialize a branch in a bzrdir, with specified files
 
1489
 
 
1490
        :param a_bzrdir: The bzrdir to initialize the branch in
 
1491
        :param utf8_files: The files to create as a list of
 
1492
            (filename, content) tuples
 
1493
        :param set_format: If True, set the format with
 
1494
            self.get_format_string.  (BzrBranch4 has its format set
 
1495
            elsewhere)
 
1496
        :return: a branch in this format
1680
1497
        """
 
1498
        mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
 
1499
        branch_transport = a_bzrdir.get_branch_transport(self)
 
1500
        lock_map = {
 
1501
            'metadir': ('lock', lockdir.LockDir),
 
1502
            'branch4': ('branch-lock', lockable_files.TransportLock),
 
1503
        }
 
1504
        lock_name, lock_class = lock_map[lock_type]
 
1505
        control_files = lockable_files.LockableFiles(branch_transport,
 
1506
            lock_name, lock_class)
 
1507
        control_files.create_lock()
 
1508
        try:
 
1509
            control_files.lock_write()
 
1510
        except errors.LockContention:
 
1511
            if lock_type != 'branch4':
 
1512
                raise
 
1513
            lock_taken = False
 
1514
        else:
 
1515
            lock_taken = True
 
1516
        if set_format:
 
1517
            utf8_files += [('format', self.get_format_string())]
 
1518
        try:
 
1519
            for (filename, content) in utf8_files:
 
1520
                branch_transport.put_bytes(
 
1521
                    filename, content,
 
1522
                    mode=a_bzrdir._get_file_mode())
 
1523
        finally:
 
1524
            if lock_taken:
 
1525
                control_files.unlock()
 
1526
        return self.open(a_bzrdir, _found=True)
 
1527
 
 
1528
    def initialize(self, a_bzrdir):
 
1529
        """Create a branch of this format in a_bzrdir."""
1681
1530
        raise NotImplementedError(self.initialize)
1682
1531
 
1683
1532
    def is_supported(self):
1701
1550
        Note that it is normal for branch to be a RemoteBranch when using tags
1702
1551
        on a RemoteBranch.
1703
1552
        """
1704
 
        return _mod_tag.DisabledTags(branch)
 
1553
        return DisabledTags(branch)
1705
1554
 
1706
1555
    def network_name(self):
1707
1556
        """A simple byte string uniquely identifying this format for RPC calls.
1713
1562
        """
1714
1563
        raise NotImplementedError(self.network_name)
1715
1564
 
1716
 
    def open(self, controldir, name=None, _found=False, ignore_fallbacks=False,
1717
 
            found_repository=None, possible_transports=None):
1718
 
        """Return the branch object for controldir.
 
1565
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
 
1566
        """Return the branch object for a_bzrdir
1719
1567
 
1720
 
        :param controldir: A ControlDir that contains a branch.
1721
 
        :param name: Name of colocated branch to open
 
1568
        :param a_bzrdir: A BzrDir that contains a branch.
1722
1569
        :param _found: a private parameter, do not use it. It is used to
1723
1570
            indicate if format probing has already be done.
1724
1571
        :param ignore_fallbacks: when set, no fallback branches will be opened
1727
1574
        raise NotImplementedError(self.open)
1728
1575
 
1729
1576
    @classmethod
1730
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1731
1577
    def register_format(klass, format):
1732
 
        """Register a metadir format.
1733
 
 
1734
 
        See MetaDirBranchFormatFactory for the ability to register a format
1735
 
        without loading the code the format needs until it is actually used.
1736
 
        """
1737
 
        format_registry.register(format)
 
1578
        """Register a metadir format."""
 
1579
        klass._formats[format.get_format_string()] = format
 
1580
        # 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__)
1738
1583
 
1739
1584
    @classmethod
1740
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1741
1585
    def set_default_format(klass, format):
1742
 
        format_registry.set_default(format)
 
1586
        klass._default_format = format
1743
1587
 
1744
1588
    def supports_set_append_revisions_only(self):
1745
1589
        """True if this format supports set_append_revisions_only."""
1749
1593
        """True if this format records a stacked-on branch."""
1750
1594
        return False
1751
1595
 
1752
 
    def supports_leaving_lock(self):
1753
 
        """True if this format supports leaving locks in place."""
1754
 
        return False # by default
1755
 
 
1756
1596
    @classmethod
1757
 
    @deprecated_method(deprecated_in((2, 4, 0)))
1758
1597
    def unregister_format(klass, format):
1759
 
        format_registry.remove(format)
 
1598
        del klass._formats[format.get_format_string()]
1760
1599
 
1761
1600
    def __str__(self):
1762
1601
        return self.get_format_description().rstrip()
1765
1604
        """True if this format supports tags stored in the branch"""
1766
1605
        return False  # by default
1767
1606
 
1768
 
    def tags_are_versioned(self):
1769
 
        """Whether the tag container for this branch versions tags."""
1770
 
        return False
1771
 
 
1772
 
    def supports_tags_referencing_ghosts(self):
1773
 
        """True if tags can reference ghost revisions."""
1774
 
        return True
1775
 
 
1776
 
 
1777
 
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1778
 
    """A factory for a BranchFormat object, permitting simple lazy registration.
1779
 
    
1780
 
    While none of the built in BranchFormats are lazy registered yet,
1781
 
    bzrlib.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
1782
 
    use it, and the bzr-loom plugin uses it as well (see
1783
 
    bzrlib.plugins.loom.formats).
1784
 
    """
1785
 
 
1786
 
    def __init__(self, format_string, module_name, member_name):
1787
 
        """Create a MetaDirBranchFormatFactory.
1788
 
 
1789
 
        :param format_string: The format string the format has.
1790
 
        :param module_name: Module to load the format class from.
1791
 
        :param member_name: Attribute name within the module for the format class.
1792
 
        """
1793
 
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
1794
 
        self._format_string = format_string
1795
 
 
1796
 
    def get_format_string(self):
1797
 
        """See BranchFormat.get_format_string."""
1798
 
        return self._format_string
1799
 
 
1800
 
    def __call__(self):
1801
 
        """Used for network_format_registry support."""
1802
 
        return self.get_obj()()
1803
 
 
1804
1607
 
1805
1608
class BranchHooks(Hooks):
1806
1609
    """A dictionary mapping hook name to a list of callables for branch hooks.
1815
1618
        These are all empty initially, because by default nothing should get
1816
1619
        notified.
1817
1620
        """
1818
 
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
1819
 
        self.add_hook('set_rh',
 
1621
        Hooks.__init__(self)
 
1622
        self.create_hook(HookPoint('set_rh',
1820
1623
            "Invoked whenever the revision history has been set via "
1821
1624
            "set_revision_history. The api signature is (branch, "
1822
1625
            "revision_history), and the branch will be write-locked. "
1823
1626
            "The set_rh hook can be expensive for bzr to trigger, a better "
1824
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
1825
 
        self.add_hook('open',
 
1627
            "hook to use is Branch.post_change_branch_tip.", (0, 15), None))
 
1628
        self.create_hook(HookPoint('open',
1826
1629
            "Called with the Branch object that has been opened after a "
1827
 
            "branch is opened.", (1, 8))
1828
 
        self.add_hook('post_push',
 
1630
            "branch is opened.", (1, 8), None))
 
1631
        self.create_hook(HookPoint('post_push',
1829
1632
            "Called after a push operation completes. post_push is called "
1830
1633
            "with a bzrlib.branch.BranchPushResult object and only runs in the "
1831
 
            "bzr client.", (0, 15))
1832
 
        self.add_hook('post_pull',
 
1634
            "bzr client.", (0, 15), None))
 
1635
        self.create_hook(HookPoint('post_pull',
1833
1636
            "Called after a pull operation completes. post_pull is called "
1834
1637
            "with a bzrlib.branch.PullResult object and only runs in the "
1835
 
            "bzr client.", (0, 15))
1836
 
        self.add_hook('pre_commit',
1837
 
            "Called after a commit is calculated but before it is "
 
1638
            "bzr client.", (0, 15), None))
 
1639
        self.create_hook(HookPoint('pre_commit',
 
1640
            "Called after a commit is calculated but before it is is "
1838
1641
            "completed. pre_commit is called with (local, master, old_revno, "
1839
1642
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1840
1643
            "). old_revid is NULL_REVISION for the first commit to a branch, "
1842
1645
            "basis revision. hooks MUST NOT modify this delta. "
1843
1646
            " future_tree is an in-memory tree obtained from "
1844
1647
            "CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1845
 
            "tree.", (0,91))
1846
 
        self.add_hook('post_commit',
 
1648
            "tree.", (0,91), None))
 
1649
        self.create_hook(HookPoint('post_commit',
1847
1650
            "Called in the bzr client after a commit has completed. "
1848
1651
            "post_commit is called with (local, master, old_revno, old_revid, "
1849
1652
            "new_revno, new_revid). old_revid is NULL_REVISION for the first "
1850
 
            "commit to a branch.", (0, 15))
1851
 
        self.add_hook('post_uncommit',
 
1653
            "commit to a branch.", (0, 15), None))
 
1654
        self.create_hook(HookPoint('post_uncommit',
1852
1655
            "Called in the bzr client after an uncommit completes. "
1853
1656
            "post_uncommit is called with (local, master, old_revno, "
1854
1657
            "old_revid, new_revno, new_revid) where local is the local branch "
1855
1658
            "or None, master is the target branch, and an empty branch "
1856
 
            "receives new_revno of 0, new_revid of None.", (0, 15))
1857
 
        self.add_hook('pre_change_branch_tip',
 
1659
            "receives new_revno of 0, new_revid of None.", (0, 15), None))
 
1660
        self.create_hook(HookPoint('pre_change_branch_tip',
1858
1661
            "Called in bzr client and server before a change to the tip of a "
1859
1662
            "branch is made. pre_change_branch_tip is called with a "
1860
1663
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1861
 
            "commit, uncommit will all trigger this hook.", (1, 6))
1862
 
        self.add_hook('post_change_branch_tip',
 
1664
            "commit, uncommit will all trigger this hook.", (1, 6), None))
 
1665
        self.create_hook(HookPoint('post_change_branch_tip',
1863
1666
            "Called in bzr client and server after a change to the tip of a "
1864
1667
            "branch is made. post_change_branch_tip is called with a "
1865
1668
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1866
 
            "commit, uncommit will all trigger this hook.", (1, 4))
1867
 
        self.add_hook('transform_fallback_location',
 
1669
            "commit, uncommit will all trigger this hook.", (1, 4), None))
 
1670
        self.create_hook(HookPoint('transform_fallback_location',
1868
1671
            "Called when a stacked branch is activating its fallback "
1869
1672
            "locations. transform_fallback_location is called with (branch, "
1870
1673
            "url), and should return a new url. Returning the same url "
1875
1678
            "fallback locations have not been activated. When there are "
1876
1679
            "multiple hooks installed for transform_fallback_location, "
1877
1680
            "all are called with the url returned from the previous hook."
1878
 
            "The order is however undefined.", (1, 9))
1879
 
        self.add_hook('automatic_tag_name',
1880
 
            "Called to determine an automatic tag name for a revision. "
1881
 
            "automatic_tag_name is called with (branch, revision_id) and "
1882
 
            "should return a tag name or None if no tag name could be "
1883
 
            "determined. The first non-None tag name returned will be used.",
1884
 
            (2, 2))
1885
 
        self.add_hook('post_branch_init',
1886
 
            "Called after new branch initialization completes. "
1887
 
            "post_branch_init is called with a "
1888
 
            "bzrlib.branch.BranchInitHookParams. "
1889
 
            "Note that init, branch and checkout (both heavyweight and "
1890
 
            "lightweight) will all trigger this hook.", (2, 2))
1891
 
        self.add_hook('post_switch',
1892
 
            "Called after a checkout switches branch. "
1893
 
            "post_switch is called with a "
1894
 
            "bzrlib.branch.SwitchHookParams.", (2, 2))
1895
 
 
 
1681
            "The order is however undefined.", (1, 9), None))
1896
1682
 
1897
1683
 
1898
1684
# install the default hooks into the Branch class.
1900
1686
 
1901
1687
 
1902
1688
class ChangeBranchTipParams(object):
1903
 
    """Object holding parameters passed to `*_change_branch_tip` hooks.
 
1689
    """Object holding parameters passed to *_change_branch_tip hooks.
1904
1690
 
1905
1691
    There are 5 fields that hooks may wish to access:
1906
1692
 
1937
1723
            self.old_revno, self.old_revid, self.new_revno, self.new_revid)
1938
1724
 
1939
1725
 
1940
 
class BranchInitHookParams(object):
1941
 
    """Object holding parameters passed to `*_branch_init` hooks.
1942
 
 
1943
 
    There are 4 fields that hooks may wish to access:
1944
 
 
1945
 
    :ivar format: the branch format
1946
 
    :ivar bzrdir: the ControlDir where the branch will be/has been initialized
1947
 
    :ivar name: name of colocated branch, if any (or None)
1948
 
    :ivar branch: the branch created
1949
 
 
1950
 
    Note that for lightweight checkouts, the bzrdir and format fields refer to
1951
 
    the checkout, hence they are different from the corresponding fields in
1952
 
    branch, which refer to the original branch.
1953
 
    """
1954
 
 
1955
 
    def __init__(self, format, controldir, name, branch):
1956
 
        """Create a group of BranchInitHook parameters.
1957
 
 
1958
 
        :param format: the branch format
1959
 
        :param controldir: the ControlDir where the branch will be/has been
1960
 
            initialized
1961
 
        :param name: name of colocated branch, if any (or None)
1962
 
        :param branch: the branch created
1963
 
 
1964
 
        Note that for lightweight checkouts, the bzrdir and format fields refer
1965
 
        to the checkout, hence they are different from the corresponding fields
1966
 
        in branch, which refer to the original branch.
1967
 
        """
1968
 
        self.format = format
1969
 
        self.bzrdir = controldir
1970
 
        self.name = name
1971
 
        self.branch = branch
1972
 
 
1973
 
    def __eq__(self, other):
1974
 
        return self.__dict__ == other.__dict__
1975
 
 
1976
 
    def __repr__(self):
1977
 
        return "<%s of %s>" % (self.__class__.__name__, self.branch)
1978
 
 
1979
 
 
1980
 
class SwitchHookParams(object):
1981
 
    """Object holding parameters passed to `*_switch` hooks.
1982
 
 
1983
 
    There are 4 fields that hooks may wish to access:
1984
 
 
1985
 
    :ivar control_dir: ControlDir of the checkout to change
1986
 
    :ivar to_branch: branch that the checkout is to reference
1987
 
    :ivar force: skip the check for local commits in a heavy checkout
1988
 
    :ivar revision_id: revision ID to switch to (or None)
1989
 
    """
1990
 
 
1991
 
    def __init__(self, control_dir, to_branch, force, revision_id):
1992
 
        """Create a group of SwitchHook parameters.
1993
 
 
1994
 
        :param control_dir: ControlDir of the checkout to change
1995
 
        :param to_branch: branch that the checkout is to reference
1996
 
        :param force: skip the check for local commits in a heavy checkout
1997
 
        :param revision_id: revision ID to switch to (or None)
1998
 
        """
1999
 
        self.control_dir = control_dir
2000
 
        self.to_branch = to_branch
2001
 
        self.force = force
2002
 
        self.revision_id = revision_id
2003
 
 
2004
 
    def __eq__(self, other):
2005
 
        return self.__dict__ == other.__dict__
2006
 
 
2007
 
    def __repr__(self):
2008
 
        return "<%s for %s to (%s, %s)>" % (self.__class__.__name__,
2009
 
            self.control_dir, self.to_branch,
2010
 
            self.revision_id)
2011
 
 
2012
 
 
2013
 
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
2014
 
    """Base class for branch formats that live in meta directories.
2015
 
    """
 
1726
class BzrBranchFormat4(BranchFormat):
 
1727
    """Bzr branch format 4.
 
1728
 
 
1729
    This format has:
 
1730
     - a revision-history file.
 
1731
     - a branch-lock lock file [ to be shared with the bzrdir ]
 
1732
    """
 
1733
 
 
1734
    def get_format_description(self):
 
1735
        """See BranchFormat.get_format_description()."""
 
1736
        return "Branch format 4"
 
1737
 
 
1738
    def initialize(self, a_bzrdir):
 
1739
        """Create a branch of this format in a_bzrdir."""
 
1740
        utf8_files = [('revision-history', ''),
 
1741
                      ('branch-name', ''),
 
1742
                      ]
 
1743
        return self._initialize_helper(a_bzrdir, utf8_files,
 
1744
                                       lock_type='branch4', set_format=False)
2016
1745
 
2017
1746
    def __init__(self):
2018
 
        BranchFormat.__init__(self)
2019
 
        bzrdir.BzrFormat.__init__(self)
2020
 
 
2021
 
    @classmethod
2022
 
    def find_format(klass, controldir, name=None):
2023
 
        """Return the format for the branch object in controldir."""
2024
 
        try:
2025
 
            transport = controldir.get_branch_transport(None, name=name)
2026
 
        except errors.NoSuchFile:
2027
 
            raise errors.NotBranchError(path=name, bzrdir=controldir)
2028
 
        try:
2029
 
            format_string = transport.get_bytes("format")
2030
 
        except errors.NoSuchFile:
2031
 
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
2032
 
        return klass._find_format(format_registry, 'branch', format_string)
 
1747
        super(BzrBranchFormat4, self).__init__()
 
1748
        self._matchingbzrdir = bzrdir.BzrDirFormat6()
 
1749
 
 
1750
    def network_name(self):
 
1751
        """The network name for this format is the control dirs disk label."""
 
1752
        return self._matchingbzrdir.get_format_string()
 
1753
 
 
1754
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
 
1755
        """See BranchFormat.open()."""
 
1756
        if not _found:
 
1757
            # we are being called directly and must probe.
 
1758
            raise NotImplementedError
 
1759
        return BzrBranch(_format=self,
 
1760
                         _control_files=a_bzrdir._control_files,
 
1761
                         a_bzrdir=a_bzrdir,
 
1762
                         _repository=a_bzrdir.open_repository())
 
1763
 
 
1764
    def __str__(self):
 
1765
        return "Bazaar-NG branch format 4"
 
1766
 
 
1767
 
 
1768
class BranchFormatMetadir(BranchFormat):
 
1769
    """Common logic for meta-dir based branch formats."""
2033
1770
 
2034
1771
    def _branch_class(self):
2035
1772
        """What class to instantiate on open calls."""
2036
1773
        raise NotImplementedError(self._branch_class)
2037
1774
 
2038
 
    def _get_initial_config(self, append_revisions_only=None):
2039
 
        if append_revisions_only:
2040
 
            return "append_revisions_only = True\n"
2041
 
        else:
2042
 
            # Avoid writing anything if append_revisions_only is disabled,
2043
 
            # as that is the default.
2044
 
            return ""
2045
 
 
2046
 
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
2047
 
                           repository=None):
2048
 
        """Initialize a branch in a bzrdir, with specified files
2049
 
 
2050
 
        :param a_bzrdir: The bzrdir to initialize the branch in
2051
 
        :param utf8_files: The files to create as a list of
2052
 
            (filename, content) tuples
2053
 
        :param name: Name of colocated branch to create, if any
2054
 
        :return: a branch in this format
 
1775
    def network_name(self):
 
1776
        """A simple byte string uniquely identifying this format for RPC calls.
 
1777
 
 
1778
        Metadir branch formats use their format string.
2055
1779
        """
2056
 
        if name is None:
2057
 
            name = a_bzrdir._get_selected_branch()
2058
 
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
2059
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2060
 
        control_files = lockable_files.LockableFiles(branch_transport,
2061
 
            'lock', lockdir.LockDir)
2062
 
        control_files.create_lock()
2063
 
        control_files.lock_write()
2064
 
        try:
2065
 
            utf8_files += [('format', self.as_string())]
2066
 
            for (filename, content) in utf8_files:
2067
 
                branch_transport.put_bytes(
2068
 
                    filename, content,
2069
 
                    mode=a_bzrdir._get_file_mode())
2070
 
        finally:
2071
 
            control_files.unlock()
2072
 
        branch = self.open(a_bzrdir, name, _found=True,
2073
 
                found_repository=repository)
2074
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2075
 
        return branch
 
1780
        return self.get_format_string()
2076
1781
 
2077
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
2078
 
            found_repository=None, possible_transports=None):
 
1782
    def open(self, a_bzrdir, _found=False, ignore_fallbacks=False):
2079
1783
        """See BranchFormat.open()."""
2080
 
        if name is None:
2081
 
            name = a_bzrdir._get_selected_branch()
2082
1784
        if not _found:
2083
 
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
 
1785
            format = BranchFormat.find_format(a_bzrdir)
2084
1786
            if format.__class__ != self.__class__:
2085
1787
                raise AssertionError("wrong format %r found for %r" %
2086
1788
                    (format, self))
2087
 
        transport = a_bzrdir.get_branch_transport(None, name=name)
2088
1789
        try:
 
1790
            transport = a_bzrdir.get_branch_transport(None)
2089
1791
            control_files = lockable_files.LockableFiles(transport, 'lock',
2090
1792
                                                         lockdir.LockDir)
2091
 
            if found_repository is None:
2092
 
                found_repository = a_bzrdir.find_repository()
2093
1793
            return self._branch_class()(_format=self,
2094
1794
                              _control_files=control_files,
2095
 
                              name=name,
2096
1795
                              a_bzrdir=a_bzrdir,
2097
 
                              _repository=found_repository,
2098
 
                              ignore_fallbacks=ignore_fallbacks,
2099
 
                              possible_transports=possible_transports)
 
1796
                              _repository=a_bzrdir.find_repository(),
 
1797
                              ignore_fallbacks=ignore_fallbacks)
2100
1798
        except errors.NoSuchFile:
2101
1799
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2102
1800
 
2103
 
    @property
2104
 
    def _matchingbzrdir(self):
2105
 
        ret = bzrdir.BzrDirMetaFormat1()
2106
 
        ret.set_branch_format(self)
2107
 
        return ret
 
1801
    def __init__(self):
 
1802
        super(BranchFormatMetadir, self).__init__()
 
1803
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
1804
        self._matchingbzrdir.set_branch_format(self)
2108
1805
 
2109
1806
    def supports_tags(self):
2110
1807
        return True
2111
1808
 
2112
 
    def supports_leaving_lock(self):
2113
 
        return True
2114
 
 
2115
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
2116
 
            basedir=None):
2117
 
        BranchFormat.check_support_status(self,
2118
 
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
2119
 
            basedir=basedir)
2120
 
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
2121
 
            recommend_upgrade=recommend_upgrade, basedir=basedir)
2122
 
 
2123
1809
 
2124
1810
class BzrBranchFormat5(BranchFormatMetadir):
2125
1811
    """Bzr branch format 5.
2137
1823
    def _branch_class(self):
2138
1824
        return BzrBranch5
2139
1825
 
2140
 
    @classmethod
2141
 
    def get_format_string(cls):
 
1826
    def get_format_string(self):
2142
1827
        """See BranchFormat.get_format_string()."""
2143
1828
        return "Bazaar-NG branch format 5\n"
2144
1829
 
2146
1831
        """See BranchFormat.get_format_description()."""
2147
1832
        return "Branch format 5"
2148
1833
 
2149
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2150
 
                   append_revisions_only=None):
 
1834
    def initialize(self, a_bzrdir):
2151
1835
        """Create a branch of this format in a_bzrdir."""
2152
 
        if append_revisions_only:
2153
 
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2154
1836
        utf8_files = [('revision-history', ''),
2155
1837
                      ('branch-name', ''),
2156
1838
                      ]
2157
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
1839
        return self._initialize_helper(a_bzrdir, utf8_files)
2158
1840
 
2159
1841
    def supports_tags(self):
2160
1842
        return False
2174
1856
    def _branch_class(self):
2175
1857
        return BzrBranch6
2176
1858
 
2177
 
    @classmethod
2178
 
    def get_format_string(cls):
 
1859
    def get_format_string(self):
2179
1860
        """See BranchFormat.get_format_string()."""
2180
1861
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
2181
1862
 
2183
1864
        """See BranchFormat.get_format_description()."""
2184
1865
        return "Branch format 6"
2185
1866
 
2186
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2187
 
                   append_revisions_only=None):
 
1867
    def initialize(self, a_bzrdir):
2188
1868
        """Create a branch of this format in a_bzrdir."""
2189
1869
        utf8_files = [('last-revision', '0 null:\n'),
2190
 
                      ('branch.conf',
2191
 
                          self._get_initial_config(append_revisions_only)),
 
1870
                      ('branch.conf', ''),
2192
1871
                      ('tags', ''),
2193
1872
                      ]
2194
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
1873
        return self._initialize_helper(a_bzrdir, utf8_files)
2195
1874
 
2196
1875
    def make_tags(self, branch):
2197
1876
        """See bzrlib.branch.BranchFormat.make_tags()."""
2198
 
        return _mod_tag.BasicTags(branch)
 
1877
        return BasicTags(branch)
2199
1878
 
2200
1879
    def supports_set_append_revisions_only(self):
2201
1880
        return True
2207
1886
    def _branch_class(self):
2208
1887
        return BzrBranch8
2209
1888
 
2210
 
    @classmethod
2211
 
    def get_format_string(cls):
 
1889
    def get_format_string(self):
2212
1890
        """See BranchFormat.get_format_string()."""
2213
1891
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2214
1892
 
2216
1894
        """See BranchFormat.get_format_description()."""
2217
1895
        return "Branch format 8"
2218
1896
 
2219
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2220
 
                   append_revisions_only=None):
 
1897
    def initialize(self, a_bzrdir):
2221
1898
        """Create a branch of this format in a_bzrdir."""
2222
1899
        utf8_files = [('last-revision', '0 null:\n'),
2223
 
                      ('branch.conf',
2224
 
                          self._get_initial_config(append_revisions_only)),
 
1900
                      ('branch.conf', ''),
2225
1901
                      ('tags', ''),
2226
1902
                      ('references', '')
2227
1903
                      ]
2228
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
1904
        return self._initialize_helper(a_bzrdir, utf8_files)
 
1905
 
 
1906
    def __init__(self):
 
1907
        super(BzrBranchFormat8, self).__init__()
 
1908
        self._matchingbzrdir.repository_format = \
 
1909
            RepositoryFormatKnitPack5RichRoot()
2229
1910
 
2230
1911
    def make_tags(self, branch):
2231
1912
        """See bzrlib.branch.BranchFormat.make_tags()."""
2232
 
        return _mod_tag.BasicTags(branch)
 
1913
        return BasicTags(branch)
2233
1914
 
2234
1915
    def supports_set_append_revisions_only(self):
2235
1916
        return True
2240
1921
    supports_reference_locations = True
2241
1922
 
2242
1923
 
2243
 
class BzrBranchFormat7(BranchFormatMetadir):
 
1924
class BzrBranchFormat7(BzrBranchFormat8):
2244
1925
    """Branch format with last-revision, tags, and a stacked location pointer.
2245
1926
 
2246
1927
    The stacked location pointer is passed down to the repository and requires
2249
1930
    This format was introduced in bzr 1.6.
2250
1931
    """
2251
1932
 
2252
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2253
 
                   append_revisions_only=None):
 
1933
    def initialize(self, a_bzrdir):
2254
1934
        """Create a branch of this format in a_bzrdir."""
2255
1935
        utf8_files = [('last-revision', '0 null:\n'),
2256
 
                      ('branch.conf',
2257
 
                          self._get_initial_config(append_revisions_only)),
 
1936
                      ('branch.conf', ''),
2258
1937
                      ('tags', ''),
2259
1938
                      ]
2260
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
 
1939
        return self._initialize_helper(a_bzrdir, utf8_files)
2261
1940
 
2262
1941
    def _branch_class(self):
2263
1942
        return BzrBranch7
2264
1943
 
2265
 
    @classmethod
2266
 
    def get_format_string(cls):
 
1944
    def get_format_string(self):
2267
1945
        """See BranchFormat.get_format_string()."""
2268
1946
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2269
1947
 
2274
1952
    def supports_set_append_revisions_only(self):
2275
1953
        return True
2276
1954
 
2277
 
    def supports_stacking(self):
2278
 
        return True
2279
 
 
2280
 
    def make_tags(self, branch):
2281
 
        """See bzrlib.branch.BranchFormat.make_tags()."""
2282
 
        return _mod_tag.BasicTags(branch)
2283
 
 
2284
1955
    supports_reference_locations = False
2285
1956
 
2286
1957
 
2287
 
class BranchReferenceFormat(BranchFormatMetadir):
 
1958
class BranchReferenceFormat(BranchFormat):
2288
1959
    """Bzr branch reference format.
2289
1960
 
2290
1961
    Branch references are used in implementing checkouts, they
2295
1966
     - a format string
2296
1967
    """
2297
1968
 
2298
 
    @classmethod
2299
 
    def get_format_string(cls):
 
1969
    def get_format_string(self):
2300
1970
        """See BranchFormat.get_format_string()."""
2301
1971
        return "Bazaar-NG Branch Reference Format 1\n"
2302
1972
 
2304
1974
        """See BranchFormat.get_format_description()."""
2305
1975
        return "Checkout reference format 1"
2306
1976
 
2307
 
    def get_reference(self, a_bzrdir, name=None):
 
1977
    def get_reference(self, a_bzrdir):
2308
1978
        """See BranchFormat.get_reference()."""
2309
 
        transport = a_bzrdir.get_branch_transport(None, name=name)
 
1979
        transport = a_bzrdir.get_branch_transport(None)
2310
1980
        return transport.get_bytes('location')
2311
1981
 
2312
 
    def set_reference(self, a_bzrdir, name, to_branch):
 
1982
    def set_reference(self, a_bzrdir, to_branch):
2313
1983
        """See BranchFormat.set_reference()."""
2314
 
        transport = a_bzrdir.get_branch_transport(None, name=name)
 
1984
        transport = a_bzrdir.get_branch_transport(None)
2315
1985
        location = transport.put_bytes('location', to_branch.base)
2316
1986
 
2317
 
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2318
 
            repository=None, append_revisions_only=None):
 
1987
    def initialize(self, a_bzrdir, target_branch=None):
2319
1988
        """Create a branch of this format in a_bzrdir."""
2320
1989
        if target_branch is None:
2321
1990
            # this format does not implement branch itself, thus the implicit
2322
1991
            # creation contract must see it as uninitializable
2323
1992
            raise errors.UninitializableFormat(self)
2324
 
        mutter('creating branch reference in %s', a_bzrdir.user_url)
2325
 
        if a_bzrdir._format.fixed_components:
2326
 
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
2327
 
        if name is None:
2328
 
            name = a_bzrdir._get_selected_branch()
2329
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
 
1993
        mutter('creating branch reference in %s', a_bzrdir.transport.base)
 
1994
        branch_transport = a_bzrdir.get_branch_transport(self)
2330
1995
        branch_transport.put_bytes('location',
2331
 
            target_branch.user_url)
2332
 
        branch_transport.put_bytes('format', self.as_string())
2333
 
        branch = self.open(a_bzrdir, name, _found=True,
 
1996
            target_branch.bzrdir.root_transport.base)
 
1997
        branch_transport.put_bytes('format', self.get_format_string())
 
1998
        return self.open(
 
1999
            a_bzrdir, _found=True,
2334
2000
            possible_transports=[target_branch.bzrdir.root_transport])
2335
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2336
 
        return branch
 
2001
 
 
2002
    def __init__(self):
 
2003
        super(BranchReferenceFormat, self).__init__()
 
2004
        self._matchingbzrdir = bzrdir.BzrDirMetaFormat1()
 
2005
        self._matchingbzrdir.set_branch_format(self)
2337
2006
 
2338
2007
    def _make_reference_clone_function(format, a_branch):
2339
2008
        """Create a clone() routine for a branch dynamically."""
2340
2009
        def clone(to_bzrdir, revision_id=None,
2341
2010
            repository_policy=None):
2342
2011
            """See Branch.clone()."""
2343
 
            return format.initialize(to_bzrdir, target_branch=a_branch)
 
2012
            return format.initialize(to_bzrdir, a_branch)
2344
2013
            # cannot obey revision_id limits when cloning a reference ...
2345
2014
            # FIXME RBC 20060210 either nuke revision_id for clone, or
2346
2015
            # emit some sort of warning/error to the caller ?!
2347
2016
        return clone
2348
2017
 
2349
 
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2350
 
             possible_transports=None, ignore_fallbacks=False,
2351
 
             found_repository=None):
 
2018
    def open(self, a_bzrdir, _found=False, location=None,
 
2019
             possible_transports=None, ignore_fallbacks=False):
2352
2020
        """Return the branch that the branch reference in a_bzrdir points at.
2353
2021
 
2354
2022
        :param a_bzrdir: A BzrDir that contains a branch.
2355
 
        :param name: Name of colocated branch to open, if any
2356
2023
        :param _found: a private parameter, do not use it. It is used to
2357
2024
            indicate if format probing has already be done.
2358
2025
        :param ignore_fallbacks: when set, no fallback branches will be opened
2362
2029
            a_bzrdir.
2363
2030
        :param possible_transports: An optional reusable transports list.
2364
2031
        """
2365
 
        if name is None:
2366
 
            name = a_bzrdir._get_selected_branch()
2367
2032
        if not _found:
2368
 
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
 
2033
            format = BranchFormat.find_format(a_bzrdir)
2369
2034
            if format.__class__ != self.__class__:
2370
2035
                raise AssertionError("wrong format %r found for %r" %
2371
2036
                    (format, self))
2372
2037
        if location is None:
2373
 
            location = self.get_reference(a_bzrdir, name)
2374
 
        real_bzrdir = controldir.ControlDir.open(
 
2038
            location = self.get_reference(a_bzrdir)
 
2039
        real_bzrdir = bzrdir.BzrDir.open(
2375
2040
            location, possible_transports=possible_transports)
2376
 
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
2377
 
            possible_transports=possible_transports)
 
2041
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks)
2378
2042
        # this changes the behaviour of result.clone to create a new reference
2379
2043
        # rather than a copy of the content of the branch.
2380
2044
        # I did not use a proxy object because that needs much more extensive
2387
2051
        return result
2388
2052
 
2389
2053
 
2390
 
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
2391
 
    """Branch format registry."""
2392
 
 
2393
 
    def __init__(self, other_registry=None):
2394
 
        super(BranchFormatRegistry, self).__init__(other_registry)
2395
 
        self._default_format = None
2396
 
 
2397
 
    def set_default(self, format):
2398
 
        self._default_format = format
2399
 
 
2400
 
    def get_default(self):
2401
 
        return self._default_format
2402
 
 
2403
 
 
2404
2054
network_format_registry = registry.FormatRegistry()
2405
2055
"""Registry of formats indexed by their network name.
2406
2056
 
2409
2059
BranchFormat.network_name() for more detail.
2410
2060
"""
2411
2061
 
2412
 
format_registry = BranchFormatRegistry(network_format_registry)
2413
 
 
2414
2062
 
2415
2063
# formats which have no format string are not discoverable
2416
2064
# and not independently creatable, so are not registered.
2418
2066
__format6 = BzrBranchFormat6()
2419
2067
__format7 = BzrBranchFormat7()
2420
2068
__format8 = BzrBranchFormat8()
2421
 
format_registry.register(__format5)
2422
 
format_registry.register(BranchReferenceFormat())
2423
 
format_registry.register(__format6)
2424
 
format_registry.register(__format7)
2425
 
format_registry.register(__format8)
2426
 
format_registry.set_default(__format7)
2427
 
 
2428
 
 
2429
 
class BranchWriteLockResult(LogicalLockResult):
2430
 
    """The result of write locking a branch.
2431
 
 
2432
 
    :ivar branch_token: The token obtained from the underlying branch lock, or
2433
 
        None.
2434
 
    :ivar unlock: A callable which will unlock the lock.
2435
 
    """
2436
 
 
2437
 
    def __init__(self, unlock, branch_token):
2438
 
        LogicalLockResult.__init__(self, unlock)
2439
 
        self.branch_token = branch_token
2440
 
 
2441
 
    def __repr__(self):
2442
 
        return "BranchWriteLockResult(%s, %s)" % (self.branch_token,
2443
 
            self.unlock)
 
2069
BranchFormat.register_format(__format5)
 
2070
BranchFormat.register_format(BranchReferenceFormat())
 
2071
BranchFormat.register_format(__format6)
 
2072
BranchFormat.register_format(__format7)
 
2073
BranchFormat.register_format(__format8)
 
2074
BranchFormat.set_default_format(__format7)
 
2075
_legacy_formats = [BzrBranchFormat4(),
 
2076
    ]
 
2077
network_format_registry.register(
 
2078
    _legacy_formats[0].network_name(), _legacy_formats[0].__class__)
2444
2079
 
2445
2080
 
2446
2081
class BzrBranch(Branch, _RelockDebugMixin):
2455
2090
    :ivar repository: Repository for this branch.
2456
2091
    :ivar base: The url of the base directory for this branch; the one
2457
2092
        containing the .bzr directory.
2458
 
    :ivar name: Optional colocated branch name as it exists in the control
2459
 
        directory.
2460
2093
    """
2461
2094
 
2462
2095
    def __init__(self, _format=None,
2463
 
                 _control_files=None, a_bzrdir=None, name=None,
2464
 
                 _repository=None, ignore_fallbacks=False,
2465
 
                 possible_transports=None):
 
2096
                 _control_files=None, a_bzrdir=None, _repository=None,
 
2097
                 ignore_fallbacks=False):
2466
2098
        """Create new branch object at a particular location."""
2467
2099
        if a_bzrdir is None:
2468
2100
            raise ValueError('a_bzrdir must be supplied')
2469
 
        if name is None:
2470
 
            raise ValueError('name must be supplied')
2471
 
        self.bzrdir = a_bzrdir
2472
 
        self._user_transport = self.bzrdir.transport.clone('..')
2473
 
        if name != "":
2474
 
            self._user_transport.set_segment_parameter(
2475
 
                "branch", urlutils.escape(name))
2476
 
        self._base = self._user_transport.base
2477
 
        self.name = name
 
2101
        else:
 
2102
            self.bzrdir = a_bzrdir
 
2103
        self._base = self.bzrdir.transport.clone('..').base
 
2104
        # XXX: We should be able to just do
 
2105
        #   self.base = self.bzrdir.root_transport.base
 
2106
        # but this does not quite work yet -- mbp 20080522
2478
2107
        self._format = _format
2479
2108
        if _control_files is None:
2480
2109
            raise ValueError('BzrBranch _control_files is None')
2481
2110
        self.control_files = _control_files
2482
2111
        self._transport = _control_files._transport
2483
2112
        self.repository = _repository
2484
 
        Branch.__init__(self, possible_transports)
 
2113
        Branch.__init__(self)
2485
2114
 
2486
2115
    def __str__(self):
2487
 
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
 
2116
        return '%s(%r)' % (self.__class__.__name__, self.base)
2488
2117
 
2489
2118
    __repr__ = __str__
2490
2119
 
2494
2123
 
2495
2124
    base = property(_get_base, doc="The URL for the root of this branch.")
2496
2125
 
2497
 
    @property
2498
 
    def user_transport(self):
2499
 
        return self._user_transport
2500
 
 
2501
2126
    def _get_config(self):
2502
 
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
2503
 
 
2504
 
    def _get_config_store(self):
2505
 
        return _mod_config.BranchStore(self)
 
2127
        return TransportConfig(self._transport, 'branch.conf')
2506
2128
 
2507
2129
    def is_locked(self):
2508
2130
        return self.control_files.is_locked()
2509
2131
 
2510
2132
    def lock_write(self, token=None):
2511
 
        """Lock the branch for write operations.
2512
 
 
2513
 
        :param token: A token to permit reacquiring a previously held and
2514
 
            preserved lock.
2515
 
        :return: A BranchWriteLockResult.
2516
 
        """
2517
2133
        if not self.is_locked():
2518
2134
            self._note_lock('w')
2519
2135
        # All-in-one needs to always unlock/lock.
2525
2141
        else:
2526
2142
            took_lock = False
2527
2143
        try:
2528
 
            return BranchWriteLockResult(self.unlock,
2529
 
                self.control_files.lock_write(token=token))
 
2144
            return self.control_files.lock_write(token=token)
2530
2145
        except:
2531
2146
            if took_lock:
2532
2147
                self.repository.unlock()
2533
2148
            raise
2534
2149
 
2535
2150
    def lock_read(self):
2536
 
        """Lock the branch for read operations.
2537
 
 
2538
 
        :return: A bzrlib.lock.LogicalLockResult.
2539
 
        """
2540
2151
        if not self.is_locked():
2541
2152
            self._note_lock('r')
2542
2153
        # All-in-one needs to always unlock/lock.
2549
2160
            took_lock = False
2550
2161
        try:
2551
2162
            self.control_files.lock_read()
2552
 
            return LogicalLockResult(self.unlock)
2553
2163
        except:
2554
2164
            if took_lock:
2555
2165
                self.repository.unlock()
2583
2193
        """See Branch.print_file."""
2584
2194
        return self.repository.print_file(file, revision_id)
2585
2195
 
 
2196
    def _write_revision_history(self, history):
 
2197
        """Factored out of set_revision_history.
 
2198
 
 
2199
        This performs the actual writing to disk.
 
2200
        It is intended to be called by BzrBranch5.set_revision_history."""
 
2201
        self._transport.put_bytes(
 
2202
            'revision-history', '\n'.join(history),
 
2203
            mode=self.bzrdir._get_file_mode())
 
2204
 
 
2205
    @needs_write_lock
 
2206
    def set_revision_history(self, rev_history):
 
2207
        """See Branch.set_revision_history."""
 
2208
        if 'evil' in debug.debug_flags:
 
2209
            mutter_callsite(3, "set_revision_history scales with history.")
 
2210
        check_not_reserved_id = _mod_revision.check_not_reserved_id
 
2211
        for rev_id in rev_history:
 
2212
            check_not_reserved_id(rev_id)
 
2213
        if Branch.hooks['post_change_branch_tip']:
 
2214
            # Don't calculate the last_revision_info() if there are no hooks
 
2215
            # that will use it.
 
2216
            old_revno, old_revid = self.last_revision_info()
 
2217
        if len(rev_history) == 0:
 
2218
            revid = _mod_revision.NULL_REVISION
 
2219
        else:
 
2220
            revid = rev_history[-1]
 
2221
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
 
2222
        self._write_revision_history(rev_history)
 
2223
        self._clear_cached_state()
 
2224
        self._cache_revision_history(rev_history)
 
2225
        for hook in Branch.hooks['set_rh']:
 
2226
            hook(self, rev_history)
 
2227
        if Branch.hooks['post_change_branch_tip']:
 
2228
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2229
 
 
2230
    def _synchronize_history(self, destination, revision_id):
 
2231
        """Synchronize last revision and revision history between branches.
 
2232
 
 
2233
        This version is most efficient when the destination is also a
 
2234
        BzrBranch5, but works for BzrBranch6 as long as the revision
 
2235
        history is the true lefthand parent history, and all of the revisions
 
2236
        are in the destination's repository.  If not, set_revision_history
 
2237
        will fail.
 
2238
 
 
2239
        :param destination: The branch to copy the history into
 
2240
        :param revision_id: The revision-id to truncate history at.  May
 
2241
          be None to copy complete history.
 
2242
        """
 
2243
        if not isinstance(destination._format, BzrBranchFormat5):
 
2244
            super(BzrBranch, self)._synchronize_history(
 
2245
                destination, revision_id)
 
2246
            return
 
2247
        if revision_id == _mod_revision.NULL_REVISION:
 
2248
            new_history = []
 
2249
        else:
 
2250
            new_history = self.revision_history()
 
2251
        if revision_id is not None and new_history != []:
 
2252
            try:
 
2253
                new_history = new_history[:new_history.index(revision_id) + 1]
 
2254
            except ValueError:
 
2255
                rev = self.repository.get_revision(revision_id)
 
2256
                new_history = rev.get_history(self.repository)[1:]
 
2257
        destination.set_revision_history(new_history)
 
2258
 
2586
2259
    @needs_write_lock
2587
2260
    def set_last_revision_info(self, revno, revision_id):
2588
 
        if not revision_id or not isinstance(revision_id, basestring):
2589
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2261
        """Set the last revision of this branch.
 
2262
 
 
2263
        The caller is responsible for checking that the revno is correct
 
2264
        for this revision id.
 
2265
 
 
2266
        It may be possible to set the branch last revision to an id not
 
2267
        present in the repository.  However, branches can also be
 
2268
        configured to check constraints on history, in which case this may not
 
2269
        be permitted.
 
2270
        """
2590
2271
        revision_id = _mod_revision.ensure_null(revision_id)
2591
 
        old_revno, old_revid = self.last_revision_info()
2592
 
        if self.get_append_revisions_only():
2593
 
            self._check_history_violation(revision_id)
2594
 
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2595
 
        self._write_last_revision_info(revno, revision_id)
2596
 
        self._clear_cached_state()
2597
 
        self._last_revision_info_cache = revno, revision_id
2598
 
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2272
        # this old format stores the full history, but this api doesn't
 
2273
        # provide it, so we must generate, and might as well check it's
 
2274
        # correct
 
2275
        history = self._lefthand_history(revision_id)
 
2276
        if len(history) != revno:
 
2277
            raise AssertionError('%d != %d' % (len(history), revno))
 
2278
        self.set_revision_history(history)
 
2279
 
 
2280
    def _gen_revision_history(self):
 
2281
        history = self._transport.get_bytes('revision-history').split('\n')
 
2282
        if history[-1:] == ['']:
 
2283
            # There shouldn't be a trailing newline, but just in case.
 
2284
            history.pop()
 
2285
        return history
 
2286
 
 
2287
    @needs_write_lock
 
2288
    def generate_revision_history(self, revision_id, last_rev=None,
 
2289
        other_branch=None):
 
2290
        """Create a new revision history that will finish with revision_id.
 
2291
 
 
2292
        :param revision_id: the new tip to use.
 
2293
        :param last_rev: The previous last_revision. If not None, then this
 
2294
            must be a ancestory of revision_id, or DivergedBranches is raised.
 
2295
        :param other_branch: The other branch that DivergedBranches should
 
2296
            raise with respect to.
 
2297
        """
 
2298
        self.set_revision_history(self._lefthand_history(revision_id,
 
2299
            last_rev, other_branch))
2599
2300
 
2600
2301
    def basis_tree(self):
2601
2302
        """See Branch.basis_tree."""
2610
2311
                pass
2611
2312
        return None
2612
2313
 
 
2314
    def _basic_push(self, target, overwrite, stop_revision):
 
2315
        """Basic implementation of push without bound branches or hooks.
 
2316
 
 
2317
        Must be called with source read locked and target write locked.
 
2318
        """
 
2319
        result = BranchPushResult()
 
2320
        result.source_branch = self
 
2321
        result.target_branch = target
 
2322
        result.old_revno, result.old_revid = target.last_revision_info()
 
2323
        self.update_references(target)
 
2324
        if result.old_revid != self.last_revision():
 
2325
            # We assume that during 'push' this repository is closer than
 
2326
            # the target.
 
2327
            graph = self.repository.get_graph(target.repository)
 
2328
            target.update_revisions(self, stop_revision,
 
2329
                overwrite=overwrite, graph=graph)
 
2330
        if self._push_should_merge_tags():
 
2331
            result.tag_conflicts = self.tags.merge_to(target.tags,
 
2332
                overwrite)
 
2333
        result.new_revno, result.new_revid = target.last_revision_info()
 
2334
        return result
 
2335
 
2613
2336
    def get_stacked_on_url(self):
2614
 
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
 
2337
        raise errors.UnstackableBranchFormat(self._format, self.base)
2615
2338
 
2616
2339
    def set_push_location(self, location):
2617
2340
        """See Branch.set_push_location."""
2626
2349
            self._transport.put_bytes('parent', url + '\n',
2627
2350
                mode=self.bzrdir._get_file_mode())
2628
2351
 
 
2352
 
 
2353
class BzrBranch5(BzrBranch):
 
2354
    """A format 5 branch. This supports new features over plain branches.
 
2355
 
 
2356
    It has support for a master_branch which is the data for bound branches.
 
2357
    """
 
2358
 
 
2359
    def get_bound_location(self):
 
2360
        try:
 
2361
            return self._transport.get_bytes('bound')[:-1]
 
2362
        except errors.NoSuchFile:
 
2363
            return None
 
2364
 
 
2365
    @needs_read_lock
 
2366
    def get_master_branch(self, possible_transports=None):
 
2367
        """Return the branch we are bound to.
 
2368
 
 
2369
        :return: Either a Branch, or None
 
2370
 
 
2371
        This could memoise the branch, but if thats done
 
2372
        it must be revalidated on each new lock.
 
2373
        So for now we just don't memoise it.
 
2374
        # RBC 20060304 review this decision.
 
2375
        """
 
2376
        bound_loc = self.get_bound_location()
 
2377
        if not bound_loc:
 
2378
            return None
 
2379
        try:
 
2380
            return Branch.open(bound_loc,
 
2381
                               possible_transports=possible_transports)
 
2382
        except (errors.NotBranchError, errors.ConnectionError), e:
 
2383
            raise errors.BoundBranchConnectionFailure(
 
2384
                    self, bound_loc, e)
 
2385
 
2629
2386
    @needs_write_lock
2630
 
    def unbind(self):
2631
 
        """If bound, unbind"""
2632
 
        return self.set_bound_location(None)
 
2387
    def set_bound_location(self, location):
 
2388
        """Set the target where this branch is bound to.
 
2389
 
 
2390
        :param location: URL to the target branch
 
2391
        """
 
2392
        if location:
 
2393
            self._transport.put_bytes('bound', location+'\n',
 
2394
                mode=self.bzrdir._get_file_mode())
 
2395
        else:
 
2396
            try:
 
2397
                self._transport.delete('bound')
 
2398
            except errors.NoSuchFile:
 
2399
                return False
 
2400
            return True
2633
2401
 
2634
2402
    @needs_write_lock
2635
2403
    def bind(self, other):
2657
2425
        # history around
2658
2426
        self.set_bound_location(other.base)
2659
2427
 
2660
 
    def get_bound_location(self):
2661
 
        try:
2662
 
            return self._transport.get_bytes('bound')[:-1]
2663
 
        except errors.NoSuchFile:
2664
 
            return None
2665
 
 
2666
 
    @needs_read_lock
2667
 
    def get_master_branch(self, possible_transports=None):
2668
 
        """Return the branch we are bound to.
2669
 
 
2670
 
        :return: Either a Branch, or None
2671
 
        """
2672
 
        if self._master_branch_cache is None:
2673
 
            self._master_branch_cache = self._get_master_branch(
2674
 
                possible_transports)
2675
 
        return self._master_branch_cache
2676
 
 
2677
 
    def _get_master_branch(self, possible_transports):
2678
 
        bound_loc = self.get_bound_location()
2679
 
        if not bound_loc:
2680
 
            return None
2681
 
        try:
2682
 
            return Branch.open(bound_loc,
2683
 
                               possible_transports=possible_transports)
2684
 
        except (errors.NotBranchError, errors.ConnectionError), e:
2685
 
            raise errors.BoundBranchConnectionFailure(
2686
 
                    self, bound_loc, e)
2687
 
 
2688
2428
    @needs_write_lock
2689
 
    def set_bound_location(self, location):
2690
 
        """Set the target where this branch is bound to.
2691
 
 
2692
 
        :param location: URL to the target branch
2693
 
        """
2694
 
        self._master_branch_cache = None
2695
 
        if location:
2696
 
            self._transport.put_bytes('bound', location+'\n',
2697
 
                mode=self.bzrdir._get_file_mode())
2698
 
        else:
2699
 
            try:
2700
 
                self._transport.delete('bound')
2701
 
            except errors.NoSuchFile:
2702
 
                return False
2703
 
            return True
 
2429
    def unbind(self):
 
2430
        """If bound, unbind"""
 
2431
        return self.set_bound_location(None)
2704
2432
 
2705
2433
    @needs_write_lock
2706
2434
    def update(self, possible_transports=None):
2719
2447
            return old_tip
2720
2448
        return None
2721
2449
 
2722
 
    def _read_last_revision_info(self):
2723
 
        revision_string = self._transport.get_bytes('last-revision')
2724
 
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2725
 
        revision_id = cache_utf8.get_cached_utf8(revision_id)
2726
 
        revno = int(revno)
2727
 
        return revno, revision_id
2728
 
 
2729
 
    def _write_last_revision_info(self, revno, revision_id):
2730
 
        """Simply write out the revision id, with no checks.
2731
 
 
2732
 
        Use set_last_revision_info to perform this safely.
2733
 
 
2734
 
        Does not update the revision_history cache.
2735
 
        """
2736
 
        revision_id = _mod_revision.ensure_null(revision_id)
2737
 
        out_string = '%d %s\n' % (revno, revision_id)
2738
 
        self._transport.put_bytes('last-revision', out_string,
2739
 
            mode=self.bzrdir._get_file_mode())
2740
 
 
2741
 
    @needs_write_lock
2742
 
    def update_feature_flags(self, updated_flags):
2743
 
        """Update the feature flags for this branch.
2744
 
 
2745
 
        :param updated_flags: Dictionary mapping feature names to necessities
2746
 
            A necessity can be None to indicate the feature should be removed
2747
 
        """
2748
 
        self._format._update_feature_flags(updated_flags)
2749
 
        self.control_transport.put_bytes('format', self._format.as_string())
2750
 
 
2751
 
 
2752
 
class FullHistoryBzrBranch(BzrBranch):
2753
 
    """Bzr branch which contains the full revision history."""
2754
 
 
2755
 
    @needs_write_lock
2756
 
    def set_last_revision_info(self, revno, revision_id):
2757
 
        if not revision_id or not isinstance(revision_id, basestring):
2758
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2759
 
        revision_id = _mod_revision.ensure_null(revision_id)
2760
 
        # this old format stores the full history, but this api doesn't
2761
 
        # provide it, so we must generate, and might as well check it's
2762
 
        # correct
2763
 
        history = self._lefthand_history(revision_id)
2764
 
        if len(history) != revno:
2765
 
            raise AssertionError('%d != %d' % (len(history), revno))
2766
 
        self._set_revision_history(history)
2767
 
 
2768
 
    def _read_last_revision_info(self):
2769
 
        rh = self._revision_history()
2770
 
        revno = len(rh)
2771
 
        if revno:
2772
 
            return (revno, rh[-1])
2773
 
        else:
2774
 
            return (0, _mod_revision.NULL_REVISION)
2775
 
 
2776
 
    @deprecated_method(deprecated_in((2, 4, 0)))
2777
 
    @needs_write_lock
2778
 
    def set_revision_history(self, rev_history):
2779
 
        """See Branch.set_revision_history."""
2780
 
        self._set_revision_history(rev_history)
2781
 
 
2782
 
    def _set_revision_history(self, rev_history):
2783
 
        if 'evil' in debug.debug_flags:
2784
 
            mutter_callsite(3, "set_revision_history scales with history.")
2785
 
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2786
 
        for rev_id in rev_history:
2787
 
            check_not_reserved_id(rev_id)
2788
 
        if Branch.hooks['post_change_branch_tip']:
2789
 
            # Don't calculate the last_revision_info() if there are no hooks
2790
 
            # that will use it.
2791
 
            old_revno, old_revid = self.last_revision_info()
2792
 
        if len(rev_history) == 0:
2793
 
            revid = _mod_revision.NULL_REVISION
2794
 
        else:
2795
 
            revid = rev_history[-1]
2796
 
        self._run_pre_change_branch_tip_hooks(len(rev_history), revid)
2797
 
        self._write_revision_history(rev_history)
2798
 
        self._clear_cached_state()
2799
 
        self._cache_revision_history(rev_history)
2800
 
        for hook in Branch.hooks['set_rh']:
2801
 
            hook(self, rev_history)
2802
 
        if Branch.hooks['post_change_branch_tip']:
2803
 
            self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2804
 
 
2805
 
    def _write_revision_history(self, history):
2806
 
        """Factored out of set_revision_history.
2807
 
 
2808
 
        This performs the actual writing to disk.
2809
 
        It is intended to be called by set_revision_history."""
2810
 
        self._transport.put_bytes(
2811
 
            'revision-history', '\n'.join(history),
2812
 
            mode=self.bzrdir._get_file_mode())
2813
 
 
2814
 
    def _gen_revision_history(self):
2815
 
        history = self._transport.get_bytes('revision-history').split('\n')
2816
 
        if history[-1:] == ['']:
2817
 
            # There shouldn't be a trailing newline, but just in case.
2818
 
            history.pop()
2819
 
        return history
2820
 
 
2821
 
    def _synchronize_history(self, destination, revision_id):
2822
 
        if not isinstance(destination, FullHistoryBzrBranch):
2823
 
            super(BzrBranch, self)._synchronize_history(
2824
 
                destination, revision_id)
2825
 
            return
2826
 
        if revision_id == _mod_revision.NULL_REVISION:
2827
 
            new_history = []
2828
 
        else:
2829
 
            new_history = self._revision_history()
2830
 
        if revision_id is not None and new_history != []:
2831
 
            try:
2832
 
                new_history = new_history[:new_history.index(revision_id) + 1]
2833
 
            except ValueError:
2834
 
                rev = self.repository.get_revision(revision_id)
2835
 
                new_history = rev.get_history(self.repository)[1:]
2836
 
        destination._set_revision_history(new_history)
2837
 
 
2838
 
    @needs_write_lock
2839
 
    def generate_revision_history(self, revision_id, last_rev=None,
2840
 
        other_branch=None):
2841
 
        """Create a new revision history that will finish with revision_id.
2842
 
 
2843
 
        :param revision_id: the new tip to use.
2844
 
        :param last_rev: The previous last_revision. If not None, then this
2845
 
            must be a ancestory of revision_id, or DivergedBranches is raised.
2846
 
        :param other_branch: The other branch that DivergedBranches should
2847
 
            raise with respect to.
2848
 
        """
2849
 
        self._set_revision_history(self._lefthand_history(revision_id,
2850
 
            last_rev, other_branch))
2851
 
 
2852
 
 
2853
 
class BzrBranch5(FullHistoryBzrBranch):
2854
 
    """A format 5 branch. This supports new features over plain branches.
2855
 
 
2856
 
    It has support for a master_branch which is the data for bound branches.
2857
 
    """
2858
 
 
2859
 
 
2860
 
class BzrBranch8(BzrBranch):
 
2450
 
 
2451
class BzrBranch8(BzrBranch5):
2861
2452
    """A branch that stores tree-reference locations."""
2862
2453
 
2863
 
    def _open_hook(self, possible_transports=None):
 
2454
    def _open_hook(self):
2864
2455
        if self._ignore_fallbacks:
2865
2456
            return
2866
 
        if possible_transports is None:
2867
 
            possible_transports = [self.bzrdir.root_transport]
2868
2457
        try:
2869
2458
            url = self.get_stacked_on_url()
2870
2459
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2878
2467
                    raise AssertionError(
2879
2468
                        "'transform_fallback_location' hook %s returned "
2880
2469
                        "None, not a URL." % hook_name)
2881
 
            self._activate_fallback_location(url,
2882
 
                possible_transports=possible_transports)
 
2470
            self._activate_fallback_location(url)
2883
2471
 
2884
2472
    def __init__(self, *args, **kwargs):
2885
2473
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2892
2480
        self._last_revision_info_cache = None
2893
2481
        self._reference_info = None
2894
2482
 
 
2483
    def _last_revision_info(self):
 
2484
        revision_string = self._transport.get_bytes('last-revision')
 
2485
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
 
2486
        revision_id = cache_utf8.get_cached_utf8(revision_id)
 
2487
        revno = int(revno)
 
2488
        return revno, revision_id
 
2489
 
 
2490
    def _write_last_revision_info(self, revno, revision_id):
 
2491
        """Simply write out the revision id, with no checks.
 
2492
 
 
2493
        Use set_last_revision_info to perform this safely.
 
2494
 
 
2495
        Does not update the revision_history cache.
 
2496
        Intended to be called by set_last_revision_info and
 
2497
        _write_revision_history.
 
2498
        """
 
2499
        revision_id = _mod_revision.ensure_null(revision_id)
 
2500
        out_string = '%d %s\n' % (revno, revision_id)
 
2501
        self._transport.put_bytes('last-revision', out_string,
 
2502
            mode=self.bzrdir._get_file_mode())
 
2503
 
 
2504
    @needs_write_lock
 
2505
    def set_last_revision_info(self, revno, revision_id):
 
2506
        revision_id = _mod_revision.ensure_null(revision_id)
 
2507
        old_revno, old_revid = self.last_revision_info()
 
2508
        if self._get_append_revisions_only():
 
2509
            self._check_history_violation(revision_id)
 
2510
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
 
2511
        self._write_last_revision_info(revno, revision_id)
 
2512
        self._clear_cached_state()
 
2513
        self._last_revision_info_cache = revno, revision_id
 
2514
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
 
2515
 
 
2516
    def _synchronize_history(self, destination, revision_id):
 
2517
        """Synchronize last revision and revision history between branches.
 
2518
 
 
2519
        :see: Branch._synchronize_history
 
2520
        """
 
2521
        # XXX: The base Branch has a fast implementation of this method based
 
2522
        # on set_last_revision_info, but BzrBranch/BzrBranch5 have a slower one
 
2523
        # that uses set_revision_history.  This class inherits from BzrBranch5,
 
2524
        # but wants the fast implementation, so it calls
 
2525
        # Branch._synchronize_history directly.
 
2526
        Branch._synchronize_history(self, destination, revision_id)
 
2527
 
2895
2528
    def _check_history_violation(self, revision_id):
2896
 
        current_revid = self.last_revision()
2897
 
        last_revision = _mod_revision.ensure_null(current_revid)
 
2529
        last_revision = _mod_revision.ensure_null(self.last_revision())
2898
2530
        if _mod_revision.is_null(last_revision):
2899
2531
            return
2900
 
        graph = self.repository.get_graph()
2901
 
        for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2902
 
            if lh_ancestor == current_revid:
2903
 
                return
2904
 
        raise errors.AppendRevisionsOnlyViolation(self.user_url)
 
2532
        if last_revision not in self._lefthand_history(revision_id):
 
2533
            raise errors.AppendRevisionsOnlyViolation(self.base)
2905
2534
 
2906
2535
    def _gen_revision_history(self):
2907
2536
        """Generate the revision history from last revision
2910
2539
        self._extend_partial_history(stop_index=last_revno-1)
2911
2540
        return list(reversed(self._partial_revision_history_cache))
2912
2541
 
 
2542
    def _write_revision_history(self, history):
 
2543
        """Factored out of set_revision_history.
 
2544
 
 
2545
        This performs the actual writing to disk, with format-specific checks.
 
2546
        It is intended to be called by BzrBranch5.set_revision_history.
 
2547
        """
 
2548
        if len(history) == 0:
 
2549
            last_revision = 'null:'
 
2550
        else:
 
2551
            if history != self._lefthand_history(history[-1]):
 
2552
                raise errors.NotLefthandHistory(history)
 
2553
            last_revision = history[-1]
 
2554
        if self._get_append_revisions_only():
 
2555
            self._check_history_violation(last_revision)
 
2556
        self._write_last_revision_info(len(history), last_revision)
 
2557
 
2913
2558
    @needs_write_lock
2914
2559
    def _set_parent_location(self, url):
2915
2560
        """Set the parent branch"""
2991
2636
        if branch_location is None:
2992
2637
            return Branch.reference_parent(self, file_id, path,
2993
2638
                                           possible_transports)
2994
 
        branch_location = urlutils.join(self.user_url, branch_location)
 
2639
        branch_location = urlutils.join(self.base, branch_location)
2995
2640
        return Branch.open(branch_location,
2996
2641
                           possible_transports=possible_transports)
2997
2642
 
3001
2646
 
3002
2647
    def set_bound_location(self, location):
3003
2648
        """See Branch.set_push_location."""
3004
 
        self._master_branch_cache = None
3005
2649
        result = None
3006
 
        conf = self.get_config_stack()
 
2650
        config = self.get_config()
3007
2651
        if location is None:
3008
 
            if not conf.get('bound'):
 
2652
            if config.get_user_option('bound') != 'True':
3009
2653
                return False
3010
2654
            else:
3011
 
                conf.set('bound', 'False')
 
2655
                config.set_user_option('bound', 'False', warn_masked=True)
3012
2656
                return True
3013
2657
        else:
3014
2658
            self._set_config_location('bound_location', location,
3015
 
                                      config=conf)
3016
 
            conf.set('bound', 'True')
 
2659
                                      config=config)
 
2660
            config.set_user_option('bound', 'True', warn_masked=True)
3017
2661
        return True
3018
2662
 
3019
2663
    def _get_bound_location(self, bound):
3020
2664
        """Return the bound location in the config file.
3021
2665
 
3022
2666
        Return None if the bound parameter does not match"""
3023
 
        conf = self.get_config_stack()
3024
 
        if conf.get('bound') != bound:
 
2667
        config = self.get_config()
 
2668
        config_bound = (config.get_user_option('bound') == 'True')
 
2669
        if config_bound != bound:
3025
2670
            return None
3026
 
        return self._get_config_location('bound_location', config=conf)
 
2671
        return self._get_config_location('bound_location', config=config)
3027
2672
 
3028
2673
    def get_bound_location(self):
3029
 
        """See Branch.get_bound_location."""
 
2674
        """See Branch.set_push_location."""
3030
2675
        return self._get_bound_location(True)
3031
2676
 
3032
2677
    def get_old_bound_location(self):
3037
2682
        # you can always ask for the URL; but you might not be able to use it
3038
2683
        # if the repo can't support stacking.
3039
2684
        ## self._check_stackable_repo()
3040
 
        # stacked_on_location is only ever defined in branch.conf, so don't
3041
 
        # waste effort reading the whole stack of config files.
3042
 
        conf = _mod_config.BranchOnlyStack(self)
3043
 
        stacked_url = self._get_config_location('stacked_on_location',
3044
 
                                                config=conf)
 
2685
        stacked_url = self._get_config_location('stacked_on_location')
3045
2686
        if stacked_url is None:
3046
2687
            raise errors.NotStacked(self)
3047
 
        return stacked_url.encode('utf-8')
 
2688
        return stacked_url
 
2689
 
 
2690
    def _get_append_revisions_only(self):
 
2691
        value = self.get_config().get_user_option('append_revisions_only')
 
2692
        return value == 'True'
 
2693
 
 
2694
    @needs_write_lock
 
2695
    def generate_revision_history(self, revision_id, last_rev=None,
 
2696
                                  other_branch=None):
 
2697
        """See BzrBranch5.generate_revision_history"""
 
2698
        history = self._lefthand_history(revision_id, last_rev, other_branch)
 
2699
        revno = len(history)
 
2700
        self.set_last_revision_info(revno, revision_id)
3048
2701
 
3049
2702
    @needs_read_lock
3050
2703
    def get_rev_id(self, revno, history=None):
3075
2728
        try:
3076
2729
            index = self._partial_revision_history_cache.index(revision_id)
3077
2730
        except ValueError:
3078
 
            try:
3079
 
                self._extend_partial_history(stop_revision=revision_id)
3080
 
            except errors.RevisionNotPresent, e:
3081
 
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
 
2731
            self._extend_partial_history(stop_revision=revision_id)
3082
2732
            index = len(self._partial_revision_history_cache) - 1
3083
 
            if index < 0:
3084
 
                raise errors.NoSuchRevision(self, revision_id)
3085
2733
            if self._partial_revision_history_cache[index] != revision_id:
3086
2734
                raise errors.NoSuchRevision(self, revision_id)
3087
2735
        return self.revno() - index
3109
2757
    """
3110
2758
 
3111
2759
    def get_stacked_on_url(self):
3112
 
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
 
2760
        raise errors.UnstackableBranchFormat(self._format, self.base)
3113
2761
 
3114
2762
 
3115
2763
######################################################################
3139
2787
    :ivar local_branch: target branch if there is a Master, else None
3140
2788
    :ivar target_branch: Target/destination branch object. (write locked)
3141
2789
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
3142
 
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3143
2790
    """
3144
2791
 
3145
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3146
2792
    def __int__(self):
3147
 
        """Return the relative change in revno.
3148
 
 
3149
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3150
 
        """
 
2793
        # DEPRECATED: pull used to return the change in revno
3151
2794
        return self.new_revno - self.old_revno
3152
2795
 
3153
2796
    def report(self, to_file):
3154
 
        tag_conflicts = getattr(self, "tag_conflicts", None)
3155
 
        tag_updates = getattr(self, "tag_updates", None)
3156
2797
        if not is_quiet():
3157
 
            if self.old_revid != self.new_revid:
 
2798
            if self.old_revid == self.new_revid:
 
2799
                to_file.write('No revisions to pull.\n')
 
2800
            else:
3158
2801
                to_file.write('Now on revision %d.\n' % self.new_revno)
3159
 
            if tag_updates:
3160
 
                to_file.write('%d tag(s) updated.\n' % len(tag_updates))
3161
 
            if self.old_revid == self.new_revid and not tag_updates:
3162
 
                if not tag_conflicts:
3163
 
                    to_file.write('No revisions or tags to pull.\n')
3164
 
                else:
3165
 
                    to_file.write('No revisions to pull.\n')
3166
2802
        self._show_tag_conficts(to_file)
3167
2803
 
3168
2804
 
3185
2821
        target, otherwise it will be None.
3186
2822
    """
3187
2823
 
3188
 
    @deprecated_method(deprecated_in((2, 3, 0)))
3189
2824
    def __int__(self):
3190
 
        """Return the relative change in revno.
3191
 
 
3192
 
        :deprecated: Use `new_revno` and `old_revno` instead.
3193
 
        """
 
2825
        # DEPRECATED: push used to return the change in revno
3194
2826
        return self.new_revno - self.old_revno
3195
2827
 
3196
2828
    def report(self, to_file):
3197
 
        # TODO: This function gets passed a to_file, but then
3198
 
        # ignores it and calls note() instead. This is also
3199
 
        # inconsistent with PullResult(), which writes to stdout.
3200
 
        # -- JRV20110901, bug #838853
3201
 
        tag_conflicts = getattr(self, "tag_conflicts", None)
3202
 
        tag_updates = getattr(self, "tag_updates", None)
3203
 
        if not is_quiet():
3204
 
            if self.old_revid != self.new_revid:
3205
 
                note(gettext('Pushed up to revision %d.') % self.new_revno)
3206
 
            if tag_updates:
3207
 
                note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
3208
 
            if self.old_revid == self.new_revid and not tag_updates:
3209
 
                if not tag_conflicts:
3210
 
                    note(gettext('No new revisions or tags to push.'))
3211
 
                else:
3212
 
                    note(gettext('No new revisions to push.'))
 
2829
        """Write a human-readable description of the result."""
 
2830
        if self.old_revid == self.new_revid:
 
2831
            note('No new revisions to push.')
 
2832
        else:
 
2833
            note('Pushed up to revision %d.' % self.new_revno)
3213
2834
        self._show_tag_conficts(to_file)
3214
2835
 
3215
2836
 
3229
2850
        :param verbose: Requests more detailed display of what was checked,
3230
2851
            if any.
3231
2852
        """
3232
 
        note(gettext('checked branch {0} format {1}').format(
3233
 
                                self.branch.user_url, self.branch._format))
 
2853
        note('checked branch %s format %s', self.branch.base,
 
2854
            self.branch._format)
3234
2855
        for error in self.errors:
3235
 
            note(gettext('found error:%s'), error)
 
2856
            note('found error:%s', error)
3236
2857
 
3237
2858
 
3238
2859
class Converter5to6(object):
3254
2875
 
3255
2876
        # Copying done; now update target format
3256
2877
        new_branch._transport.put_bytes('format',
3257
 
            format.as_string(),
 
2878
            format.get_format_string(),
3258
2879
            mode=new_branch.bzrdir._get_file_mode())
3259
2880
 
3260
2881
        # Clean up old files
3273
2894
        format = BzrBranchFormat7()
3274
2895
        branch._set_config_location('stacked_on_location', '')
3275
2896
        # update target format
3276
 
        branch._transport.put_bytes('format', format.as_string())
 
2897
        branch._transport.put_bytes('format', format.get_format_string())
3277
2898
 
3278
2899
 
3279
2900
class Converter7to8(object):
3280
 
    """Perform an in-place upgrade of format 7 to format 8"""
 
2901
    """Perform an in-place upgrade of format 6 to format 7"""
3281
2902
 
3282
2903
    def convert(self, branch):
3283
2904
        format = BzrBranchFormat8()
3284
2905
        branch._transport.put_bytes('references', '')
3285
2906
        # update target format
3286
 
        branch._transport.put_bytes('format', format.as_string())
 
2907
        branch._transport.put_bytes('format', format.get_format_string())
 
2908
 
 
2909
 
 
2910
def _run_with_write_locked_target(target, callable, *args, **kwargs):
 
2911
    """Run ``callable(*args, **kwargs)``, write-locking target for the
 
2912
    duration.
 
2913
 
 
2914
    _run_with_write_locked_target will attempt to release the lock it acquires.
 
2915
 
 
2916
    If an exception is raised by callable, then that exception *will* be
 
2917
    propagated, even if the unlock attempt raises its own error.  Thus
 
2918
    _run_with_write_locked_target should be preferred to simply doing::
 
2919
 
 
2920
        target.lock_write()
 
2921
        try:
 
2922
            return callable(*args, **kwargs)
 
2923
        finally:
 
2924
            target.unlock()
 
2925
 
 
2926
    """
 
2927
    # This is very similar to bzrlib.decorators.needs_write_lock.  Perhaps they
 
2928
    # should share code?
 
2929
    target.lock_write()
 
2930
    try:
 
2931
        result = callable(*args, **kwargs)
 
2932
    except:
 
2933
        exc_info = sys.exc_info()
 
2934
        try:
 
2935
            target.unlock()
 
2936
        finally:
 
2937
            raise exc_info[0], exc_info[1], exc_info[2]
 
2938
    else:
 
2939
        target.unlock()
 
2940
        return result
3287
2941
 
3288
2942
 
3289
2943
class InterBranch(InterObject):
3297
2951
    _optimisers = []
3298
2952
    """The available optimised InterBranch types."""
3299
2953
 
3300
 
    @classmethod
3301
 
    def _get_branch_formats_to_test(klass):
3302
 
        """Return an iterable of format tuples for testing.
3303
 
        
3304
 
        :return: An iterable of (from_format, to_format) to use when testing
3305
 
            this InterBranch class. Each InterBranch class should define this
3306
 
            method itself.
3307
 
        """
3308
 
        raise NotImplementedError(klass._get_branch_formats_to_test)
 
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)
3309
2958
 
3310
 
    @needs_write_lock
3311
2959
    def pull(self, overwrite=False, stop_revision=None,
3312
2960
             possible_transports=None, local=False):
3313
2961
        """Mirror source into target branch.
3318
2966
        """
3319
2967
        raise NotImplementedError(self.pull)
3320
2968
 
3321
 
    @needs_write_lock
3322
 
    def push(self, overwrite=False, stop_revision=None, lossy=False,
 
2969
    def update_revisions(self, stop_revision=None, overwrite=False,
 
2970
                         graph=None):
 
2971
        """Pull in new perfect-fit revisions.
 
2972
 
 
2973
        :param stop_revision: Updated until the given revision
 
2974
        :param overwrite: Always set the branch pointer, rather than checking
 
2975
            to see if it is a proper descendant.
 
2976
        :param graph: A Graph object that can be used to query history
 
2977
            information. This can be None.
 
2978
        :return: None
 
2979
        """
 
2980
        raise NotImplementedError(self.update_revisions)
 
2981
 
 
2982
    def push(self, overwrite=False, stop_revision=None,
3323
2983
             _override_hook_source_branch=None):
3324
2984
        """Mirror the source branch into the target branch.
3325
2985
 
3327
2987
        """
3328
2988
        raise NotImplementedError(self.push)
3329
2989
 
3330
 
    @needs_write_lock
3331
 
    def copy_content_into(self, revision_id=None):
3332
 
        """Copy the content of source into target
3333
 
 
3334
 
        revision_id: if not None, the revision history in the new branch will
3335
 
                     be truncated to end with revision_id.
3336
 
        """
3337
 
        raise NotImplementedError(self.copy_content_into)
3338
 
 
3339
 
    @needs_write_lock
3340
 
    def fetch(self, stop_revision=None, limit=None):
3341
 
        """Fetch revisions.
3342
 
 
3343
 
        :param stop_revision: Last revision to fetch
3344
 
        :param limit: Optional rough limit of revisions to fetch
3345
 
        """
3346
 
        raise NotImplementedError(self.fetch)
3347
 
 
3348
2990
 
3349
2991
class GenericInterBranch(InterBranch):
3350
 
    """InterBranch implementation that uses public Branch functions."""
3351
 
 
3352
 
    @classmethod
3353
 
    def is_compatible(klass, source, target):
3354
 
        # GenericBranch uses the public API, so always compatible
3355
 
        return True
3356
 
 
3357
 
    @classmethod
3358
 
    def _get_branch_formats_to_test(klass):
3359
 
        return [(format_registry.get_default(), format_registry.get_default())]
3360
 
 
3361
 
    @classmethod
3362
 
    def unwrap_format(klass, format):
3363
 
        if isinstance(format, remote.RemoteBranchFormat):
3364
 
            format._ensure_real()
3365
 
            return format._custom_format
3366
 
        return format
3367
 
 
3368
 
    @needs_write_lock
3369
 
    def copy_content_into(self, revision_id=None):
3370
 
        """Copy the content of source into target
3371
 
 
3372
 
        revision_id: if not None, the revision history in the new branch will
3373
 
                     be truncated to end with revision_id.
3374
 
        """
3375
 
        self.source.update_references(self.target)
3376
 
        self.source._synchronize_history(self.target, revision_id)
3377
 
        try:
3378
 
            parent = self.source.get_parent()
3379
 
        except errors.InaccessibleParent, e:
3380
 
            mutter('parent was not accessible to copy: %s', e)
3381
 
        else:
3382
 
            if parent:
3383
 
                self.target.set_parent(parent)
3384
 
        if self.source._push_should_merge_tags():
3385
 
            self.source.tags.merge_to(self.target.tags)
3386
 
 
3387
 
    @needs_write_lock
3388
 
    def fetch(self, stop_revision=None, limit=None):
3389
 
        if self.target.base == self.source.base:
3390
 
            return (0, [])
 
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
 
 
2999
    def update_revisions(self, stop_revision=None, overwrite=False,
 
3000
        graph=None):
 
3001
        """See InterBranch.update_revisions()."""
3391
3002
        self.source.lock_read()
3392
3003
        try:
3393
 
            fetch_spec_factory = fetch.FetchSpecFactory()
3394
 
            fetch_spec_factory.source_branch = self.source
3395
 
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3396
 
            fetch_spec_factory.source_repo = self.source.repository
3397
 
            fetch_spec_factory.target_repo = self.target.repository
3398
 
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3399
 
            fetch_spec_factory.limit = limit
3400
 
            fetch_spec = fetch_spec_factory.make_fetch_spec()
3401
 
            return self.target.repository.fetch(self.source.repository,
3402
 
                fetch_spec=fetch_spec)
 
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)
3403
3039
        finally:
3404
3040
            self.source.unlock()
3405
3041
 
3406
 
    @needs_write_lock
3407
 
    def _update_revisions(self, stop_revision=None, overwrite=False,
3408
 
            graph=None):
3409
 
        other_revno, other_last_revision = self.source.last_revision_info()
3410
 
        stop_revno = None # unknown
3411
 
        if stop_revision is None:
3412
 
            stop_revision = other_last_revision
3413
 
            if _mod_revision.is_null(stop_revision):
3414
 
                # if there are no commits, we're done.
3415
 
                return
3416
 
            stop_revno = other_revno
3417
 
 
3418
 
        # what's the current last revision, before we fetch [and change it
3419
 
        # possibly]
3420
 
        last_rev = _mod_revision.ensure_null(self.target.last_revision())
3421
 
        # we fetch here so that we don't process data twice in the common
3422
 
        # case of having something to pull, and so that the check for
3423
 
        # already merged can operate on the just fetched graph, which will
3424
 
        # be cached in memory.
3425
 
        self.fetch(stop_revision=stop_revision)
3426
 
        # Check to see if one is an ancestor of the other
3427
 
        if not overwrite:
3428
 
            if graph is None:
3429
 
                graph = self.target.repository.get_graph()
3430
 
            if self.target._check_if_descendant_or_diverged(
3431
 
                    stop_revision, last_rev, graph, self.source):
3432
 
                # stop_revision is a descendant of last_rev, but we aren't
3433
 
                # overwriting, so we're done.
3434
 
                return
3435
 
        if stop_revno is None:
3436
 
            if graph is None:
3437
 
                graph = self.target.repository.get_graph()
3438
 
            this_revno, this_last_revision = \
3439
 
                    self.target.last_revision_info()
3440
 
            stop_revno = graph.find_distance_to_null(stop_revision,
3441
 
                            [(other_last_revision, other_revno),
3442
 
                             (this_last_revision, this_revno)])
3443
 
        self.target.set_last_revision_info(stop_revno, stop_revision)
3444
 
 
3445
 
    @needs_write_lock
3446
3042
    def pull(self, overwrite=False, stop_revision=None,
3447
 
             possible_transports=None, run_hooks=True,
3448
 
             _override_hook_target=None, local=False):
3449
 
        """Pull from source into self, updating my master if any.
3450
 
 
3451
 
        :param run_hooks: Private parameter - if false, this branch
3452
 
            is being called because it's the master of the primary branch,
3453
 
            so it should not run its hooks.
3454
 
        """
3455
 
        bound_location = self.target.get_bound_location()
3456
 
        if local and not bound_location:
3457
 
            raise errors.LocalRequiresBoundBranch()
3458
 
        master_branch = None
3459
 
        source_is_master = False
3460
 
        if bound_location:
3461
 
            # bound_location comes from a config file, some care has to be
3462
 
            # taken to relate it to source.user_url
3463
 
            normalized = urlutils.normalize_url(bound_location)
3464
 
            try:
3465
 
                relpath = self.source.user_transport.relpath(normalized)
3466
 
                source_is_master = (relpath == '')
3467
 
            except (errors.PathNotChild, errors.InvalidURL):
3468
 
                source_is_master = False
3469
 
        if not local and bound_location and not source_is_master:
3470
 
            # not pulling from master, so we need to update master.
3471
 
            master_branch = self.target.get_master_branch(possible_transports)
3472
 
            master_branch.lock_write()
3473
 
        try:
3474
 
            if master_branch:
3475
 
                # pull from source into master.
3476
 
                master_branch.pull(self.source, overwrite, stop_revision,
3477
 
                    run_hooks=False)
3478
 
            return self._pull(overwrite,
3479
 
                stop_revision, _hook_master=master_branch,
3480
 
                run_hooks=run_hooks,
3481
 
                _override_hook_target=_override_hook_target,
3482
 
                merge_tags_to_master=not source_is_master)
3483
 
        finally:
3484
 
            if master_branch:
3485
 
                master_branch.unlock()
3486
 
 
3487
 
    def push(self, overwrite=False, stop_revision=None, lossy=False,
3488
 
             _override_hook_source_branch=None):
3489
 
        """See InterBranch.push.
3490
 
 
3491
 
        This is the basic concrete implementation of push()
3492
 
 
3493
 
        :param _override_hook_source_branch: If specified, run the hooks
3494
 
            passing this Branch as the source, rather than self.  This is for
3495
 
            use of RemoteBranch, where push is delegated to the underlying
3496
 
            vfs-based Branch.
3497
 
        """
3498
 
        if lossy:
3499
 
            raise errors.LossyPushToSameVCS(self.source, self.target)
3500
 
        # TODO: Public option to disable running hooks - should be trivial but
3501
 
        # needs tests.
3502
 
 
3503
 
        op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
3504
 
        op.add_cleanup(self.source.lock_read().unlock)
3505
 
        op.add_cleanup(self.target.lock_write().unlock)
3506
 
        return op.run(overwrite, stop_revision,
3507
 
            _override_hook_source_branch=_override_hook_source_branch)
3508
 
 
3509
 
    def _basic_push(self, overwrite, stop_revision):
3510
 
        """Basic implementation of push without bound branches or hooks.
3511
 
 
3512
 
        Must be called with source read locked and target write locked.
3513
 
        """
3514
 
        result = BranchPushResult()
3515
 
        result.source_branch = self.source
3516
 
        result.target_branch = self.target
3517
 
        result.old_revno, result.old_revid = self.target.last_revision_info()
3518
 
        self.source.update_references(self.target)
3519
 
        if result.old_revid != stop_revision:
3520
 
            # We assume that during 'push' this repository is closer than
3521
 
            # the target.
3522
 
            graph = self.source.repository.get_graph(self.target.repository)
3523
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3524
 
                    graph=graph)
3525
 
        if self.source._push_should_merge_tags():
3526
 
            result.tag_updates, result.tag_conflicts = (
3527
 
                self.source.tags.merge_to(self.target.tags, overwrite))
3528
 
        result.new_revno, result.new_revid = self.target.last_revision_info()
3529
 
        return result
3530
 
 
3531
 
    def _push_with_bound_branches(self, operation, overwrite, stop_revision,
3532
 
            _override_hook_source_branch=None):
3533
 
        """Push from source into target, and into target's master if any.
3534
 
        """
3535
 
        def _run_hooks():
3536
 
            if _override_hook_source_branch:
3537
 
                result.source_branch = _override_hook_source_branch
3538
 
            for hook in Branch.hooks['post_push']:
3539
 
                hook(result)
3540
 
 
3541
 
        bound_location = self.target.get_bound_location()
3542
 
        if bound_location and self.target.base != bound_location:
3543
 
            # there is a master branch.
3544
 
            #
3545
 
            # XXX: Why the second check?  Is it even supported for a branch to
3546
 
            # be bound to itself? -- mbp 20070507
3547
 
            master_branch = self.target.get_master_branch()
3548
 
            master_branch.lock_write()
3549
 
            operation.add_cleanup(master_branch.unlock)
3550
 
            # push into the master from the source branch.
3551
 
            master_inter = InterBranch.get(self.source, master_branch)
3552
 
            master_inter._basic_push(overwrite, stop_revision)
3553
 
            # and push into the target branch from the source. Note that
3554
 
            # we push from the source branch again, because it's considered
3555
 
            # the highest bandwidth repository.
3556
 
            result = self._basic_push(overwrite, stop_revision)
3557
 
            result.master_branch = master_branch
3558
 
            result.local_branch = self.target
3559
 
        else:
3560
 
            master_branch = None
3561
 
            # no master branch
3562
 
            result = self._basic_push(overwrite, stop_revision)
3563
 
            # TODO: Why set master_branch and local_branch if there's no
3564
 
            # binding?  Maybe cleaner to just leave them unset? -- mbp
3565
 
            # 20070504
3566
 
            result.master_branch = self.target
3567
 
            result.local_branch = None
3568
 
        _run_hooks()
3569
 
        return result
3570
 
 
3571
 
    def _pull(self, overwrite=False, stop_revision=None,
3572
3043
             possible_transports=None, _hook_master=None, run_hooks=True,
3573
 
             _override_hook_target=None, local=False,
3574
 
             merge_tags_to_master=True):
 
3044
             _override_hook_target=None, local=False):
3575
3045
        """See Branch.pull.
3576
3046
 
3577
 
        This function is the core worker, used by GenericInterBranch.pull to
3578
 
        avoid duplication when pulling source->master and source->local.
3579
 
 
3580
3047
        :param _hook_master: Private parameter - set the branch to
3581
3048
            be supplied as the master to pull hooks.
3582
3049
        :param run_hooks: Private parameter - if false, this branch
3583
3050
            is being called because it's the master of the primary branch,
3584
3051
            so it should not run its hooks.
3585
 
            is being called because it's the master of the primary branch,
3586
 
            so it should not run its hooks.
3587
3052
        :param _override_hook_target: Private parameter - set the branch to be
3588
3053
            supplied as the target_branch to pull hooks.
3589
3054
        :param local: Only update the local branch, and not the bound branch.
3608
3073
            # -- JRV20090506
3609
3074
            result.old_revno, result.old_revid = \
3610
3075
                self.target.last_revision_info()
3611
 
            self._update_revisions(stop_revision, overwrite=overwrite,
3612
 
                graph=graph)
 
3076
            self.target.update_revisions(self.source, stop_revision,
 
3077
                overwrite=overwrite, graph=graph)
3613
3078
            # TODO: The old revid should be specified when merging tags, 
3614
3079
            # so a tags implementation that versions tags can only 
3615
3080
            # pull in the most recent changes. -- JRV20090506
3616
 
            result.tag_updates, result.tag_conflicts = (
3617
 
                self.source.tags.merge_to(self.target.tags, overwrite,
3618
 
                    ignore_master=not merge_tags_to_master))
 
3081
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
 
3082
                overwrite)
3619
3083
            result.new_revno, result.new_revid = self.target.last_revision_info()
3620
3084
            if _hook_master:
3621
3085
                result.master_branch = _hook_master
3630
3094
            self.source.unlock()
3631
3095
        return result
3632
3096
 
 
3097
    def push(self, overwrite=False, stop_revision=None,
 
3098
             _override_hook_source_branch=None):
 
3099
        """See InterBranch.push.
 
3100
 
 
3101
        This is the basic concrete implementation of push()
 
3102
 
 
3103
        :param _override_hook_source_branch: If specified, run
 
3104
        the hooks passing this Branch as the source, rather than self.
 
3105
        This is for use of RemoteBranch, where push is delegated to the
 
3106
        underlying vfs-based Branch.
 
3107
        """
 
3108
        # TODO: Public option to disable running hooks - should be trivial but
 
3109
        # needs tests.
 
3110
        self.source.lock_read()
 
3111
        try:
 
3112
            return _run_with_write_locked_target(
 
3113
                self.target, self._push_with_bound_branches, overwrite,
 
3114
                stop_revision,
 
3115
                _override_hook_source_branch=_override_hook_source_branch)
 
3116
        finally:
 
3117
            self.source.unlock()
 
3118
 
 
3119
    def _push_with_bound_branches(self, overwrite, stop_revision,
 
3120
            _override_hook_source_branch=None):
 
3121
        """Push from source into target, and into target's master if any.
 
3122
        """
 
3123
        def _run_hooks():
 
3124
            if _override_hook_source_branch:
 
3125
                result.source_branch = _override_hook_source_branch
 
3126
            for hook in Branch.hooks['post_push']:
 
3127
                hook(result)
 
3128
 
 
3129
        bound_location = self.target.get_bound_location()
 
3130
        if bound_location and self.target.base != bound_location:
 
3131
            # there is a master branch.
 
3132
            #
 
3133
            # XXX: Why the second check?  Is it even supported for a branch to
 
3134
            # be bound to itself? -- mbp 20070507
 
3135
            master_branch = self.target.get_master_branch()
 
3136
            master_branch.lock_write()
 
3137
            try:
 
3138
                # push into the master from the source branch.
 
3139
                self.source._basic_push(master_branch, overwrite, stop_revision)
 
3140
                # and push into the target branch from the source. Note that we
 
3141
                # push from the source branch again, because its considered the
 
3142
                # highest bandwidth repository.
 
3143
                result = self.source._basic_push(self.target, overwrite,
 
3144
                    stop_revision)
 
3145
                result.master_branch = master_branch
 
3146
                result.local_branch = self.target
 
3147
                _run_hooks()
 
3148
                return result
 
3149
            finally:
 
3150
                master_branch.unlock()
 
3151
        else:
 
3152
            # no master branch
 
3153
            result = self.source._basic_push(self.target, overwrite,
 
3154
                stop_revision)
 
3155
            # TODO: Why set master_branch and local_branch if there's no
 
3156
            # binding?  Maybe cleaner to just leave them unset? -- mbp
 
3157
            # 20070504
 
3158
            result.master_branch = self.target
 
3159
            result.local_branch = None
 
3160
            _run_hooks()
 
3161
            return result
 
3162
 
 
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,
 
3177
             _override_hook_target=None, local=False):
 
3178
        """Pull from source into self, updating my master if any.
 
3179
 
 
3180
        :param run_hooks: Private parameter - if false, this branch
 
3181
            is being called because it's the master of the primary branch,
 
3182
            so it should not run its hooks.
 
3183
        """
 
3184
        bound_location = self.target.get_bound_location()
 
3185
        if local and not bound_location:
 
3186
            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()
 
3192
        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)
 
3201
        finally:
 
3202
            if master_branch:
 
3203
                master_branch.unlock()
 
3204
 
3633
3205
 
3634
3206
InterBranch.register_optimiser(GenericInterBranch)
 
3207
InterBranch.register_optimiser(InterToBranch5)