~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

Abbreviate pack_stat struct format to '>6L'

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
 
18
18
from cStringIO import StringIO
19
 
import sys
20
19
 
21
20
from bzrlib.lazy_import import lazy_import
22
21
lazy_import(globals(), """
23
 
from itertools import chain
 
22
import itertools
24
23
from bzrlib import (
25
24
        bzrdir,
26
25
        cache_utf8,
 
26
        cleanup,
27
27
        config as _mod_config,
28
28
        debug,
29
29
        errors,
35
35
        repository,
36
36
        revision as _mod_revision,
37
37
        rio,
38
 
        symbol_versioning,
 
38
        tag as _mod_tag,
39
39
        transport,
40
 
        tsort,
41
40
        ui,
42
41
        urlutils,
43
42
        )
44
 
from bzrlib.config import BranchConfig, TransportConfig
45
 
from bzrlib.tag import (
46
 
    BasicTags,
47
 
    DisabledTags,
48
 
    )
 
43
from bzrlib.i18n import gettext, ngettext
49
44
""")
50
45
 
51
46
from bzrlib import (
67
62
from bzrlib.trace import mutter, mutter_callsite, note, is_quiet
68
63
 
69
64
 
70
 
BZR_BRANCH_FORMAT_4 = "Bazaar-NG branch, format 0.0.4\n"
71
 
BZR_BRANCH_FORMAT_5 = "Bazaar-NG branch, format 5\n"
72
 
BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
73
 
 
74
 
 
75
65
class Branch(controldir.ControlComponent):
76
66
    """Branch holding a history of revisions.
77
67
 
223
213
 
224
214
        :return: A bzrlib.config.BranchConfig.
225
215
        """
226
 
        return BranchConfig(self)
 
216
        return _mod_config.BranchConfig(self)
 
217
 
 
218
    def get_config_stack(self):
 
219
        """Get a bzrlib.config.BranchStack for this Branch.
 
220
 
 
221
        This can then be used to get and set configuration options for the
 
222
        branch.
 
223
 
 
224
        :return: A bzrlib.config.BranchStack.
 
225
        """
 
226
        return _mod_config.BranchStack(self)
227
227
 
228
228
    def _get_config(self):
229
229
        """Get the concrete config for just the config in this branch.
456
456
            after. If None, the rest of history is included.
457
457
        :param stop_rule: if stop_revision_id is not None, the precise rule
458
458
            to use for termination:
 
459
 
459
460
            * 'exclude' - leave the stop revision out of the result (default)
460
461
            * 'include' - the stop revision is the last item in the result
461
462
            * 'with-merges' - include the stop revision and all of its
463
464
            * 'with-merges-without-common-ancestry' - filter out revisions 
464
465
              that are in both ancestries
465
466
        :param direction: either 'reverse' or 'forward':
 
467
 
466
468
            * reverse means return the start_revision_id first, i.e.
467
469
              start at the most recent revision and go backwards in history
468
470
            * forward returns tuples in the opposite order to reverse.
512
514
        rev_iter = iter(merge_sorted_revisions)
513
515
        if start_revision_id is not None:
514
516
            for node in rev_iter:
515
 
                rev_id = node.key[-1]
 
517
                rev_id = node.key
516
518
                if rev_id != start_revision_id:
517
519
                    continue
518
520
                else:
519
521
                    # The decision to include the start or not
520
522
                    # depends on the stop_rule if a stop is provided
521
523
                    # so pop this node back into the iterator
522
 
                    rev_iter = chain(iter([node]), rev_iter)
 
524
                    rev_iter = itertools.chain(iter([node]), rev_iter)
523
525
                    break
524
526
        if stop_revision_id is None:
525
527
            # Yield everything
526
528
            for node in rev_iter:
527
 
                rev_id = node.key[-1]
 
529
                rev_id = node.key
528
530
                yield (rev_id, node.merge_depth, node.revno,
529
531
                       node.end_of_merge)
530
532
        elif stop_rule == 'exclude':
531
533
            for node in rev_iter:
532
 
                rev_id = node.key[-1]
 
534
                rev_id = node.key
533
535
                if rev_id == stop_revision_id:
534
536
                    return
535
537
                yield (rev_id, node.merge_depth, node.revno,
536
538
                       node.end_of_merge)
537
539
        elif stop_rule == 'include':
538
540
            for node in rev_iter:
539
 
                rev_id = node.key[-1]
 
541
                rev_id = node.key
540
542
                yield (rev_id, node.merge_depth, node.revno,
541
543
                       node.end_of_merge)
542
544
                if rev_id == stop_revision_id:
548
550
            ancestors = graph.find_unique_ancestors(start_revision_id,
549
551
                                                    [stop_revision_id])
550
552
            for node in rev_iter:
551
 
                rev_id = node.key[-1]
 
553
                rev_id = node.key
552
554
                if rev_id not in ancestors:
553
555
                    continue
554
556
                yield (rev_id, node.merge_depth, node.revno,
564
566
            reached_stop_revision_id = False
565
567
            revision_id_whitelist = []
566
568
            for node in rev_iter:
567
 
                rev_id = node.key[-1]
 
569
                rev_id = node.key
568
570
                if rev_id == left_parent:
569
571
                    # reached the left parent after the stop_revision
570
572
                    return
650
652
        """
651
653
        raise errors.UpgradeRequired(self.user_url)
652
654
 
 
655
    def get_append_revisions_only(self):
 
656
        """Whether it is only possible to append revisions to the history.
 
657
        """
 
658
        if not self._format.supports_set_append_revisions_only():
 
659
            return False
 
660
        return self.get_config(
 
661
            ).get_user_option_as_bool('append_revisions_only')
 
662
 
653
663
    def set_append_revisions_only(self, enabled):
654
664
        if not self._format.supports_set_append_revisions_only():
655
665
            raise errors.UpgradeRequired(self.user_url)
669
679
        raise errors.UnsupportedOperation(self.get_reference_info, self)
670
680
 
671
681
    @needs_write_lock
672
 
    def fetch(self, from_branch, last_revision=None):
 
682
    def fetch(self, from_branch, last_revision=None, limit=None):
673
683
        """Copy revisions from from_branch into this branch.
674
684
 
675
685
        :param from_branch: Where to copy from.
676
686
        :param last_revision: What revision to stop at (None for at the end
677
687
                              of the branch.
 
688
        :param limit: Optional rough limit of revisions to fetch
678
689
        :return: None
679
690
        """
680
 
        return InterBranch.get(from_branch, self).fetch(last_revision)
 
691
        return InterBranch.get(from_branch, self).fetch(last_revision, limit=limit)
681
692
 
682
693
    def get_bound_location(self):
683
694
        """Return the URL of the branch we are bound to.
776
787
        configured to check constraints on history, in which case this may not
777
788
        be permitted.
778
789
        """
779
 
        raise NotImplementedError(self.last_revision_info)
 
790
        raise NotImplementedError(self.set_last_revision_info)
780
791
 
781
792
    @needs_write_lock
782
793
    def generate_revision_history(self, revision_id, last_rev=None,
783
794
                                  other_branch=None):
784
795
        """See Branch.generate_revision_history"""
785
 
        # FIXME: This shouldn't have to fetch the entire history
786
 
        history = self._lefthand_history(revision_id, last_rev, other_branch)
787
 
        revno = len(history)
 
796
        graph = self.repository.get_graph()
 
797
        (last_revno, last_revid) = self.last_revision_info()
 
798
        known_revision_ids = [
 
799
            (last_revid, last_revno),
 
800
            (_mod_revision.NULL_REVISION, 0),
 
801
            ]
 
802
        if last_rev is not None:
 
803
            if not graph.is_ancestor(last_rev, revision_id):
 
804
                # our previous tip is not merged into stop_revision
 
805
                raise errors.DivergedBranches(self, other_branch)
 
806
        revno = graph.find_distance_to_null(revision_id, known_revision_ids)
788
807
        self.set_last_revision_info(revno, revision_id)
789
 
        self._cache_revision_history(history)
790
808
 
791
809
    @needs_write_lock
792
810
    def set_parent(self, url):
841
859
        """
842
860
        pb = ui.ui_factory.nested_progress_bar()
843
861
        try:
844
 
            pb.update("Unstacking")
 
862
            pb.update(gettext("Unstacking"))
845
863
            # The basic approach here is to fetch the tip of the branch,
846
864
            # including all available ghosts, from the existing stacked
847
865
            # repository into a new repository object without the fallbacks. 
924
942
 
925
943
        :seealso: Branch._get_tags_bytes.
926
944
        """
927
 
        return _run_with_write_locked_target(self, self._set_tags_bytes_locked,
928
 
                bytes)
 
945
        op = cleanup.OperationWithCleanups(self._set_tags_bytes_locked)
 
946
        op.add_cleanup(self.lock_write().unlock)
 
947
        return op.run_simple(bytes)
929
948
 
930
949
    def _set_tags_bytes_locked(self, bytes):
931
950
        self._tags_bytes = bytes
1098
1117
            stop_revision=stop_revision,
1099
1118
            possible_transports=possible_transports, *args, **kwargs)
1100
1119
 
1101
 
    def push(self, target, overwrite=False, stop_revision=None, *args,
1102
 
        **kwargs):
 
1120
    def push(self, target, overwrite=False, stop_revision=None, lossy=False,
 
1121
            *args, **kwargs):
1103
1122
        """Mirror this branch into target.
1104
1123
 
1105
1124
        This branch is considered to be 'local', having low latency.
1106
1125
        """
1107
1126
        return InterBranch.get(self, target).push(overwrite, stop_revision,
1108
 
            *args, **kwargs)
1109
 
 
1110
 
    def lossy_push(self, target, stop_revision=None):
1111
 
        """Push deltas into another branch.
1112
 
 
1113
 
        :note: This does not, like push, retain the revision ids from 
1114
 
            the source branch and will, rather than adding bzr-specific 
1115
 
            metadata, push only those semantics of the revision that can be 
1116
 
            natively represented by this branch' VCS.
1117
 
 
1118
 
        :param target: Target branch
1119
 
        :param stop_revision: Revision to push, defaults to last revision.
1120
 
        :return: BranchPushResult with an extra member revidmap: 
1121
 
            A dictionary mapping revision ids from the target branch 
1122
 
            to new revision ids in the target branch, for each 
1123
 
            revision that was pushed.
1124
 
        """
1125
 
        inter = InterBranch.get(self, target)
1126
 
        lossy_push = getattr(inter, "lossy_push", None)
1127
 
        if lossy_push is None:
1128
 
            raise errors.LossyPushToSameVCS(self, target)
1129
 
        return lossy_push(stop_revision)
 
1127
            lossy, *args, **kwargs)
1130
1128
 
1131
1129
    def basis_tree(self):
1132
1130
        """Return `Tree` object for last revision."""
1305
1303
            if repository_policy is not None:
1306
1304
                repository_policy.configure_branch(result)
1307
1305
            self.copy_content_into(result, revision_id=revision_id)
1308
 
            master_branch = self.get_master_branch()
1309
 
            if master_branch is None:
 
1306
            master_url = self.get_bound_location()
 
1307
            if master_url is None:
1310
1308
                result.set_parent(self.bzrdir.root_transport.base)
1311
1309
            else:
1312
 
                result.set_parent(master_branch.bzrdir.root_transport.base)
 
1310
                result.set_parent(master_url)
1313
1311
        finally:
1314
1312
            result.unlock()
1315
1313
        return result
1393
1391
        # specific check.
1394
1392
        return result
1395
1393
 
1396
 
    def _get_checkout_format(self):
 
1394
    def _get_checkout_format(self, lightweight=False):
1397
1395
        """Return the most suitable metadir for a checkout of this branch.
1398
1396
        Weaves are used if this branch's repository uses weaves.
1399
1397
        """
1434
1432
        :param to_location: The url to produce the checkout at
1435
1433
        :param revision_id: The revision to check out
1436
1434
        :param lightweight: If True, produce a lightweight checkout, otherwise,
1437
 
        produce a bound branch (heavyweight checkout)
 
1435
            produce a bound branch (heavyweight checkout)
1438
1436
        :param accelerator_tree: A tree which can be used for retrieving file
1439
1437
            contents more quickly than the revision tree, i.e. a workingtree.
1440
1438
            The revision tree will be used for cases where accelerator_tree's
1445
1443
        """
1446
1444
        t = transport.get_transport(to_location)
1447
1445
        t.ensure_base()
 
1446
        format = self._get_checkout_format(lightweight=lightweight)
1448
1447
        if lightweight:
1449
 
            format = self._get_checkout_format()
1450
1448
            checkout = format.initialize_on_transport(t)
1451
1449
            from_branch = BranchReferenceFormat().initialize(checkout, 
1452
1450
                target_branch=self)
1453
1451
        else:
1454
 
            format = self._get_checkout_format()
1455
1452
            checkout_branch = bzrdir.BzrDir.create_branch_convenience(
1456
1453
                to_location, force_new_tree=False, format=format)
1457
1454
            checkout = checkout_branch.bzrdir
1486
1483
 
1487
1484
    def reference_parent(self, file_id, path, possible_transports=None):
1488
1485
        """Return the parent branch for a tree-reference file_id
 
1486
 
1489
1487
        :param file_id: The file_id of the tree reference
1490
1488
        :param path: The path of the file_id in the tree
1491
1489
        :return: A branch associated with the file_id
1556
1554
        # For bzr native formats must_fetch is just the tip, and if_present_fetch
1557
1555
        # are the tags.
1558
1556
        must_fetch = set([self.last_revision()])
1559
 
        try:
1560
 
            if_present_fetch = set(self.tags.get_reverse_tag_dict())
1561
 
        except errors.TagsNotSupported:
1562
 
            if_present_fetch = set()
 
1557
        if_present_fetch = set()
 
1558
        c = self.get_config()
 
1559
        include_tags = c.get_user_option_as_bool('branch.fetch_tags',
 
1560
                                                 default=False)
 
1561
        if include_tags:
 
1562
            try:
 
1563
                if_present_fetch = set(self.tags.get_reverse_tag_dict())
 
1564
            except errors.TagsNotSupported:
 
1565
                pass
1563
1566
        must_fetch.discard(_mod_revision.NULL_REVISION)
1564
1567
        if_present_fetch.discard(_mod_revision.NULL_REVISION)
1565
1568
        return must_fetch, if_present_fetch
1583
1586
    object will be created every time regardless.
1584
1587
    """
1585
1588
 
1586
 
    can_set_append_revisions_only = True
1587
 
 
1588
1589
    def __eq__(self, other):
1589
1590
        return self.__class__ is other.__class__
1590
1591
 
1662
1663
        for hook in hooks:
1663
1664
            hook(params)
1664
1665
 
1665
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
1666
    def initialize(self, a_bzrdir, name=None, repository=None,
 
1667
                   append_revisions_only=None):
1666
1668
        """Create a branch of this format in a_bzrdir.
1667
1669
        
1668
1670
        :param name: Name of the colocated branch to create.
1690
1692
        Note that it is normal for branch to be a RemoteBranch when using tags
1691
1693
        on a RemoteBranch.
1692
1694
        """
1693
 
        return DisabledTags(branch)
 
1695
        return _mod_tag.DisabledTags(branch)
1694
1696
 
1695
1697
    def network_name(self):
1696
1698
        """A simple byte string uniquely identifying this format for RPC calls.
1754
1756
        """True if this format supports tags stored in the branch"""
1755
1757
        return False  # by default
1756
1758
 
 
1759
    def tags_are_versioned(self):
 
1760
        """Whether the tag container for this branch versions tags."""
 
1761
        return False
 
1762
 
 
1763
    def supports_tags_referencing_ghosts(self):
 
1764
        """True if tags can reference ghost revisions."""
 
1765
        return True
 
1766
 
1757
1767
 
1758
1768
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1759
1769
    """A factory for a BranchFormat object, permitting simple lazy registration.
1881
1891
 
1882
1892
 
1883
1893
class ChangeBranchTipParams(object):
1884
 
    """Object holding parameters passed to *_change_branch_tip hooks.
 
1894
    """Object holding parameters passed to `*_change_branch_tip` hooks.
1885
1895
 
1886
1896
    There are 5 fields that hooks may wish to access:
1887
1897
 
1919
1929
 
1920
1930
 
1921
1931
class BranchInitHookParams(object):
1922
 
    """Object holding parameters passed to *_branch_init hooks.
 
1932
    """Object holding parameters passed to `*_branch_init` hooks.
1923
1933
 
1924
1934
    There are 4 fields that hooks may wish to access:
1925
1935
 
1959
1969
 
1960
1970
 
1961
1971
class SwitchHookParams(object):
1962
 
    """Object holding parameters passed to *_switch hooks.
 
1972
    """Object holding parameters passed to `*_switch` hooks.
1963
1973
 
1964
1974
    There are 4 fields that hooks may wish to access:
1965
1975
 
1998
2008
        """What class to instantiate on open calls."""
1999
2009
        raise NotImplementedError(self._branch_class)
2000
2010
 
 
2011
    def _get_initial_config(self, append_revisions_only=None):
 
2012
        if append_revisions_only:
 
2013
            return "append_revisions_only = True\n"
 
2014
        else:
 
2015
            # Avoid writing anything if append_revisions_only is disabled,
 
2016
            # as that is the default.
 
2017
            return ""
 
2018
 
2001
2019
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
2002
2020
                           repository=None):
2003
2021
        """Initialize a branch in a bzrdir, with specified files
2093
2111
        """See BranchFormat.get_format_description()."""
2094
2112
        return "Branch format 5"
2095
2113
 
2096
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2114
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2115
                   append_revisions_only=None):
2097
2116
        """Create a branch of this format in a_bzrdir."""
 
2117
        if append_revisions_only:
 
2118
            raise errors.UpgradeRequired(a_bzrdir.user_url)
2098
2119
        utf8_files = [('revision-history', ''),
2099
2120
                      ('branch-name', ''),
2100
2121
                      ]
2126
2147
        """See BranchFormat.get_format_description()."""
2127
2148
        return "Branch format 6"
2128
2149
 
2129
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2150
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2151
                   append_revisions_only=None):
2130
2152
        """Create a branch of this format in a_bzrdir."""
2131
2153
        utf8_files = [('last-revision', '0 null:\n'),
2132
 
                      ('branch.conf', ''),
 
2154
                      ('branch.conf',
 
2155
                          self._get_initial_config(append_revisions_only)),
2133
2156
                      ('tags', ''),
2134
2157
                      ]
2135
2158
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2136
2159
 
2137
2160
    def make_tags(self, branch):
2138
2161
        """See bzrlib.branch.BranchFormat.make_tags()."""
2139
 
        return BasicTags(branch)
 
2162
        return _mod_tag.BasicTags(branch)
2140
2163
 
2141
2164
    def supports_set_append_revisions_only(self):
2142
2165
        return True
2156
2179
        """See BranchFormat.get_format_description()."""
2157
2180
        return "Branch format 8"
2158
2181
 
2159
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2182
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2183
                   append_revisions_only=None):
2160
2184
        """Create a branch of this format in a_bzrdir."""
2161
2185
        utf8_files = [('last-revision', '0 null:\n'),
2162
 
                      ('branch.conf', ''),
 
2186
                      ('branch.conf',
 
2187
                          self._get_initial_config(append_revisions_only)),
2163
2188
                      ('tags', ''),
2164
2189
                      ('references', '')
2165
2190
                      ]
2167
2192
 
2168
2193
    def make_tags(self, branch):
2169
2194
        """See bzrlib.branch.BranchFormat.make_tags()."""
2170
 
        return BasicTags(branch)
 
2195
        return _mod_tag.BasicTags(branch)
2171
2196
 
2172
2197
    def supports_set_append_revisions_only(self):
2173
2198
        return True
2187
2212
    This format was introduced in bzr 1.6.
2188
2213
    """
2189
2214
 
2190
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2215
    def initialize(self, a_bzrdir, name=None, repository=None,
 
2216
                   append_revisions_only=None):
2191
2217
        """Create a branch of this format in a_bzrdir."""
2192
2218
        utf8_files = [('last-revision', '0 null:\n'),
2193
 
                      ('branch.conf', ''),
 
2219
                      ('branch.conf',
 
2220
                          self._get_initial_config(append_revisions_only)),
2194
2221
                      ('tags', ''),
2195
2222
                      ]
2196
2223
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2214
2241
 
2215
2242
    def make_tags(self, branch):
2216
2243
        """See bzrlib.branch.BranchFormat.make_tags()."""
2217
 
        return BasicTags(branch)
 
2244
        return _mod_tag.BasicTags(branch)
2218
2245
 
2219
2246
    supports_reference_locations = False
2220
2247
 
2249
2276
        location = transport.put_bytes('location', to_branch.base)
2250
2277
 
2251
2278
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2252
 
            repository=None):
 
2279
            repository=None, append_revisions_only=None):
2253
2280
        """Create a branch of this format in a_bzrdir."""
2254
2281
        if target_branch is None:
2255
2282
            # this format does not implement branch itself, thus the implicit
2256
2283
            # creation contract must see it as uninitializable
2257
2284
            raise errors.UninitializableFormat(self)
2258
2285
        mutter('creating branch reference in %s', a_bzrdir.user_url)
 
2286
        if a_bzrdir._format.fixed_components:
 
2287
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
2259
2288
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2260
2289
        branch_transport.put_bytes('location',
2261
2290
            target_branch.bzrdir.user_url)
2430
2459
    base = property(_get_base, doc="The URL for the root of this branch.")
2431
2460
 
2432
2461
    def _get_config(self):
2433
 
        return TransportConfig(self._transport, 'branch.conf')
 
2462
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
2434
2463
 
2435
2464
    def is_locked(self):
2436
2465
        return self.control_files.is_locked()
2517
2546
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2518
2547
        revision_id = _mod_revision.ensure_null(revision_id)
2519
2548
        old_revno, old_revid = self.last_revision_info()
2520
 
        if self._get_append_revisions_only():
 
2549
        if self.get_append_revisions_only():
2521
2550
            self._check_history_violation(revision_id)
2522
2551
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2523
2552
        self._write_last_revision_info(revno, revision_id)
2808
2837
        self._reference_info = None
2809
2838
 
2810
2839
    def _check_history_violation(self, revision_id):
2811
 
        last_revision = _mod_revision.ensure_null(self.last_revision())
 
2840
        current_revid = self.last_revision()
 
2841
        last_revision = _mod_revision.ensure_null(current_revid)
2812
2842
        if _mod_revision.is_null(last_revision):
2813
2843
            return
2814
 
        if last_revision not in self._lefthand_history(revision_id):
2815
 
            raise errors.AppendRevisionsOnlyViolation(self.user_url)
 
2844
        graph = self.repository.get_graph()
 
2845
        for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
 
2846
            if lh_ancestor == current_revid:
 
2847
                return
 
2848
        raise errors.AppendRevisionsOnlyViolation(self.user_url)
2816
2849
 
2817
2850
    def _gen_revision_history(self):
2818
2851
        """Generate the revision history from last revision
2949
2982
        # you can always ask for the URL; but you might not be able to use it
2950
2983
        # if the repo can't support stacking.
2951
2984
        ## self._check_stackable_repo()
2952
 
        stacked_url = self._get_config_location('stacked_on_location')
 
2985
        # stacked_on_location is only ever defined in branch.conf, so don't
 
2986
        # waste effort reading the whole stack of config files.
 
2987
        config = self.get_config()._get_branch_data_config()
 
2988
        stacked_url = self._get_config_location('stacked_on_location',
 
2989
            config=config)
2953
2990
        if stacked_url is None:
2954
2991
            raise errors.NotStacked(self)
2955
2992
        return stacked_url
2956
2993
 
2957
 
    def _get_append_revisions_only(self):
2958
 
        return self.get_config(
2959
 
            ).get_user_option_as_bool('append_revisions_only')
2960
 
 
2961
2994
    @needs_read_lock
2962
2995
    def get_rev_id(self, revno, history=None):
2963
2996
        """Find the revision id of the specified revno."""
3049
3082
    :ivar local_branch: target branch if there is a Master, else None
3050
3083
    :ivar target_branch: Target/destination branch object. (write locked)
3051
3084
    :ivar tag_conflicts: A list of tag conflicts, see BasicTags.merge_to
 
3085
    :ivar tag_updates: A dict with new tags, see BasicTags.merge_to
3052
3086
    """
3053
3087
 
3054
3088
    @deprecated_method(deprecated_in((2, 3, 0)))
3060
3094
        return self.new_revno - self.old_revno
3061
3095
 
3062
3096
    def report(self, to_file):
 
3097
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
3098
        tag_updates = getattr(self, "tag_updates", None)
3063
3099
        if not is_quiet():
3064
 
            if self.old_revid == self.new_revid:
3065
 
                to_file.write('No revisions to pull.\n')
3066
 
            else:
 
3100
            if self.old_revid != self.new_revid:
3067
3101
                to_file.write('Now on revision %d.\n' % self.new_revno)
 
3102
            if tag_updates:
 
3103
                to_file.write('%d tag(s) updated.\n' % len(tag_updates))
 
3104
            if self.old_revid == self.new_revid and not tag_updates:
 
3105
                if not tag_conflicts:
 
3106
                    to_file.write('No revisions or tags to pull.\n')
 
3107
                else:
 
3108
                    to_file.write('No revisions to pull.\n')
3068
3109
        self._show_tag_conficts(to_file)
3069
3110
 
3070
3111
 
3096
3137
        return self.new_revno - self.old_revno
3097
3138
 
3098
3139
    def report(self, to_file):
3099
 
        """Write a human-readable description of the result."""
3100
 
        if self.old_revid == self.new_revid:
3101
 
            note('No new revisions to push.')
3102
 
        else:
3103
 
            note('Pushed up to revision %d.' % self.new_revno)
 
3140
        # TODO: This function gets passed a to_file, but then
 
3141
        # ignores it and calls note() instead. This is also
 
3142
        # inconsistent with PullResult(), which writes to stdout.
 
3143
        # -- JRV20110901, bug #838853
 
3144
        tag_conflicts = getattr(self, "tag_conflicts", None)
 
3145
        tag_updates = getattr(self, "tag_updates", None)
 
3146
        if not is_quiet():
 
3147
            if self.old_revid != self.new_revid:
 
3148
                note(gettext('Pushed up to revision %d.') % self.new_revno)
 
3149
            if tag_updates:
 
3150
                note(ngettext('%d tag updated.', '%d tags updated.', len(tag_updates)) % len(tag_updates))
 
3151
            if self.old_revid == self.new_revid and not tag_updates:
 
3152
                if not tag_conflicts:
 
3153
                    note(gettext('No new revisions or tags to push.'))
 
3154
                else:
 
3155
                    note(gettext('No new revisions to push.'))
3104
3156
        self._show_tag_conficts(to_file)
3105
3157
 
3106
3158
 
3120
3172
        :param verbose: Requests more detailed display of what was checked,
3121
3173
            if any.
3122
3174
        """
3123
 
        note('checked branch %s format %s', self.branch.user_url,
3124
 
            self.branch._format)
 
3175
        note(gettext('checked branch {0} format {1}').format(
 
3176
                                self.branch.user_url, self.branch._format))
3125
3177
        for error in self.errors:
3126
 
            note('found error:%s', error)
 
3178
            note(gettext('found error:%s'), error)
3127
3179
 
3128
3180
 
3129
3181
class Converter5to6(object):
3168
3220
 
3169
3221
 
3170
3222
class Converter7to8(object):
3171
 
    """Perform an in-place upgrade of format 6 to format 7"""
 
3223
    """Perform an in-place upgrade of format 7 to format 8"""
3172
3224
 
3173
3225
    def convert(self, branch):
3174
3226
        format = BzrBranchFormat8()
3177
3229
        branch._transport.put_bytes('format', format.get_format_string())
3178
3230
 
3179
3231
 
3180
 
def _run_with_write_locked_target(target, callable, *args, **kwargs):
3181
 
    """Run ``callable(*args, **kwargs)``, write-locking target for the
3182
 
    duration.
3183
 
 
3184
 
    _run_with_write_locked_target will attempt to release the lock it acquires.
3185
 
 
3186
 
    If an exception is raised by callable, then that exception *will* be
3187
 
    propagated, even if the unlock attempt raises its own error.  Thus
3188
 
    _run_with_write_locked_target should be preferred to simply doing::
3189
 
 
3190
 
        target.lock_write()
3191
 
        try:
3192
 
            return callable(*args, **kwargs)
3193
 
        finally:
3194
 
            target.unlock()
3195
 
 
3196
 
    """
3197
 
    # This is very similar to bzrlib.decorators.needs_write_lock.  Perhaps they
3198
 
    # should share code?
3199
 
    target.lock_write()
3200
 
    try:
3201
 
        result = callable(*args, **kwargs)
3202
 
    except:
3203
 
        exc_info = sys.exc_info()
3204
 
        try:
3205
 
            target.unlock()
3206
 
        finally:
3207
 
            raise exc_info[0], exc_info[1], exc_info[2]
3208
 
    else:
3209
 
        target.unlock()
3210
 
        return result
3211
 
 
3212
 
 
3213
3232
class InterBranch(InterObject):
3214
3233
    """This class represents operations taking place between two branches.
3215
3234
 
3243
3262
        raise NotImplementedError(self.pull)
3244
3263
 
3245
3264
    @needs_write_lock
3246
 
    def push(self, overwrite=False, stop_revision=None,
 
3265
    def push(self, overwrite=False, stop_revision=None, lossy=False,
3247
3266
             _override_hook_source_branch=None):
3248
3267
        """Mirror the source branch into the target branch.
3249
3268
 
3261
3280
        raise NotImplementedError(self.copy_content_into)
3262
3281
 
3263
3282
    @needs_write_lock
3264
 
    def fetch(self, stop_revision=None):
 
3283
    def fetch(self, stop_revision=None, limit=None):
3265
3284
        """Fetch revisions.
3266
3285
 
3267
3286
        :param stop_revision: Last revision to fetch
 
3287
        :param limit: Optional rough limit of revisions to fetch
3268
3288
        """
3269
3289
        raise NotImplementedError(self.fetch)
3270
3290
 
3308
3328
            self.source.tags.merge_to(self.target.tags)
3309
3329
 
3310
3330
    @needs_write_lock
3311
 
    def fetch(self, stop_revision=None):
 
3331
    def fetch(self, stop_revision=None, limit=None):
3312
3332
        if self.target.base == self.source.base:
3313
3333
            return (0, [])
3314
3334
        self.source.lock_read()
3319
3339
            fetch_spec_factory.source_repo = self.source.repository
3320
3340
            fetch_spec_factory.target_repo = self.target.repository
3321
3341
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
 
3342
            fetch_spec_factory.limit = limit
3322
3343
            fetch_spec = fetch_spec_factory.make_fetch_spec()
3323
3344
            return self.target.repository.fetch(self.source.repository,
3324
3345
                fetch_spec=fetch_spec)
3378
3399
        if local and not bound_location:
3379
3400
            raise errors.LocalRequiresBoundBranch()
3380
3401
        master_branch = None
3381
 
        source_is_master = (self.source.user_url == bound_location)
 
3402
        source_is_master = False
 
3403
        if bound_location:
 
3404
            # bound_location comes from a config file, some care has to be
 
3405
            # taken to relate it to source.user_url
 
3406
            normalized = urlutils.normalize_url(bound_location)
 
3407
            try:
 
3408
                relpath = self.source.user_transport.relpath(normalized)
 
3409
                source_is_master = (relpath == '')
 
3410
            except (errors.PathNotChild, errors.InvalidURL):
 
3411
                source_is_master = False
3382
3412
        if not local and bound_location and not source_is_master:
3383
3413
            # not pulling from master, so we need to update master.
3384
3414
            master_branch = self.target.get_master_branch(possible_transports)
3397
3427
            if master_branch:
3398
3428
                master_branch.unlock()
3399
3429
 
3400
 
    def push(self, overwrite=False, stop_revision=None,
 
3430
    def push(self, overwrite=False, stop_revision=None, lossy=False,
3401
3431
             _override_hook_source_branch=None):
3402
3432
        """See InterBranch.push.
3403
3433
 
3404
3434
        This is the basic concrete implementation of push()
3405
3435
 
3406
 
        :param _override_hook_source_branch: If specified, run
3407
 
        the hooks passing this Branch as the source, rather than self.
3408
 
        This is for use of RemoteBranch, where push is delegated to the
3409
 
        underlying vfs-based Branch.
 
3436
        :param _override_hook_source_branch: If specified, run the hooks
 
3437
            passing this Branch as the source, rather than self.  This is for
 
3438
            use of RemoteBranch, where push is delegated to the underlying
 
3439
            vfs-based Branch.
3410
3440
        """
 
3441
        if lossy:
 
3442
            raise errors.LossyPushToSameVCS(self.source, self.target)
3411
3443
        # TODO: Public option to disable running hooks - should be trivial but
3412
3444
        # needs tests.
3413
 
        self.source.lock_read()
3414
 
        try:
3415
 
            return _run_with_write_locked_target(
3416
 
                self.target, self._push_with_bound_branches, overwrite,
3417
 
                stop_revision,
3418
 
                _override_hook_source_branch=_override_hook_source_branch)
3419
 
        finally:
3420
 
            self.source.unlock()
 
3445
 
 
3446
        op = cleanup.OperationWithCleanups(self._push_with_bound_branches)
 
3447
        op.add_cleanup(self.source.lock_read().unlock)
 
3448
        op.add_cleanup(self.target.lock_write().unlock)
 
3449
        return op.run(overwrite, stop_revision,
 
3450
            _override_hook_source_branch=_override_hook_source_branch)
3421
3451
 
3422
3452
    def _basic_push(self, overwrite, stop_revision):
3423
3453
        """Basic implementation of push without bound branches or hooks.
3436
3466
            self._update_revisions(stop_revision, overwrite=overwrite,
3437
3467
                    graph=graph)
3438
3468
        if self.source._push_should_merge_tags():
3439
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3440
 
                overwrite)
 
3469
            result.tag_updates, result.tag_conflicts = (
 
3470
                self.source.tags.merge_to(self.target.tags, overwrite))
3441
3471
        result.new_revno, result.new_revid = self.target.last_revision_info()
3442
3472
        return result
3443
3473
 
3444
 
    def _push_with_bound_branches(self, overwrite, stop_revision,
 
3474
    def _push_with_bound_branches(self, operation, overwrite, stop_revision,
3445
3475
            _override_hook_source_branch=None):
3446
3476
        """Push from source into target, and into target's master if any.
3447
3477
        """
3459
3489
            # be bound to itself? -- mbp 20070507
3460
3490
            master_branch = self.target.get_master_branch()
3461
3491
            master_branch.lock_write()
3462
 
            try:
3463
 
                # push into the master from the source branch.
3464
 
                master_inter = InterBranch.get(self.source, master_branch)
3465
 
                master_inter._basic_push(overwrite, stop_revision)
3466
 
                # and push into the target branch from the source. Note that
3467
 
                # we push from the source branch again, because it's considered
3468
 
                # the highest bandwidth repository.
3469
 
                result = self._basic_push(overwrite, stop_revision)
3470
 
                result.master_branch = master_branch
3471
 
                result.local_branch = self.target
3472
 
                _run_hooks()
3473
 
                return result
3474
 
            finally:
3475
 
                master_branch.unlock()
 
3492
            operation.add_cleanup(master_branch.unlock)
 
3493
            # push into the master from the source branch.
 
3494
            master_inter = InterBranch.get(self.source, master_branch)
 
3495
            master_inter._basic_push(overwrite, stop_revision)
 
3496
            # and push into the target branch from the source. Note that
 
3497
            # we push from the source branch again, because it's considered
 
3498
            # the highest bandwidth repository.
 
3499
            result = self._basic_push(overwrite, stop_revision)
 
3500
            result.master_branch = master_branch
 
3501
            result.local_branch = self.target
3476
3502
        else:
 
3503
            master_branch = None
3477
3504
            # no master branch
3478
3505
            result = self._basic_push(overwrite, stop_revision)
3479
3506
            # TODO: Why set master_branch and local_branch if there's no
3481
3508
            # 20070504
3482
3509
            result.master_branch = self.target
3483
3510
            result.local_branch = None
3484
 
            _run_hooks()
3485
 
            return result
 
3511
        _run_hooks()
 
3512
        return result
3486
3513
 
3487
3514
    def _pull(self, overwrite=False, stop_revision=None,
3488
3515
             possible_transports=None, _hook_master=None, run_hooks=True,
3529
3556
            # TODO: The old revid should be specified when merging tags, 
3530
3557
            # so a tags implementation that versions tags can only 
3531
3558
            # pull in the most recent changes. -- JRV20090506
3532
 
            result.tag_conflicts = self.source.tags.merge_to(self.target.tags,
3533
 
                overwrite, ignore_master=not merge_tags_to_master)
 
3559
            result.tag_updates, result.tag_conflicts = (
 
3560
                self.source.tags.merge_to(self.target.tags, overwrite,
 
3561
                    ignore_master=not merge_tags_to_master))
3534
3562
            result.new_revno, result.new_revid = self.target.last_revision_info()
3535
3563
            if _hook_master:
3536
3564
                result.master_branch = _hook_master