~bzr/ubuntu/hardy/bzr/beta-ppa

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Max Bowsher
  • Date: 2011-04-30 17:14:51 UTC
  • mfrom: (3876.1.10 jaunty)
  • Revision ID: maxb@f2s.com-20110430171451-74ydto59tdotsxa6
MergeĀ 2.4.0~beta2-2

Show diffs side-by-side

added added

removed removed

Lines of Context:
42
42
        urlutils,
43
43
        )
44
44
from bzrlib.config import BranchConfig, TransportConfig
45
 
from bzrlib.repofmt.pack_repo import RepositoryFormatKnitPack5RichRoot
46
45
from bzrlib.tag import (
47
46
    BasicTags,
48
47
    DisabledTags,
57
56
    needs_write_lock,
58
57
    only_raises,
59
58
    )
60
 
from bzrlib.hooks import HookPoint, Hooks
 
59
from bzrlib.hooks import Hooks
61
60
from bzrlib.inter import InterObject
62
61
from bzrlib.lock import _RelockDebugMixin, LogicalLockResult
63
62
from bzrlib import registry
79
78
    :ivar base:
80
79
        Base directory/url of the branch; using control_url and
81
80
        control_transport is more standardized.
82
 
 
83
 
    hooks: An instance of BranchHooks.
 
81
    :ivar hooks: An instance of BranchHooks.
 
82
    :ivar _master_branch_cache: cached result of get_master_branch, see
 
83
        _clear_cached_state.
84
84
    """
85
85
    # this is really an instance variable - FIXME move it there
86
86
    # - RBC 20060112
102
102
        self._partial_revision_history_cache = []
103
103
        self._tags_bytes = None
104
104
        self._last_revision_info_cache = None
 
105
        self._master_branch_cache = None
105
106
        self._merge_sorted_revisions_cache = None
106
107
        self._open_hook()
107
108
        hooks = Branch.hooks['open']
668
669
        raise errors.UnsupportedOperation(self.get_reference_info, self)
669
670
 
670
671
    @needs_write_lock
671
 
    def fetch(self, from_branch, last_revision=None, fetch_spec=None):
 
672
    def fetch(self, from_branch, last_revision=None):
672
673
        """Copy revisions from from_branch into this branch.
673
674
 
674
675
        :param from_branch: Where to copy from.
675
676
        :param last_revision: What revision to stop at (None for at the end
676
677
                              of the branch.
677
 
        :param fetch_spec: If specified, a SearchResult or
678
 
            PendingAncestryResult that describes which revisions to copy.  This
679
 
            allows copying multiple heads at once.  Mutually exclusive with
680
 
            last_revision.
681
678
        :return: None
682
679
        """
683
 
        if fetch_spec is not None and last_revision is not None:
684
 
            raise AssertionError(
685
 
                "fetch_spec and last_revision are mutually exclusive.")
686
 
        if self.base == from_branch.base:
687
 
            return (0, [])
688
 
        from_branch.lock_read()
689
 
        try:
690
 
            if last_revision is None and fetch_spec is None:
691
 
                last_revision = from_branch.last_revision()
692
 
                last_revision = _mod_revision.ensure_null(last_revision)
693
 
            return self.repository.fetch(from_branch.repository,
694
 
                                         revision_id=last_revision,
695
 
                                         fetch_spec=fetch_spec)
696
 
        finally:
697
 
            from_branch.unlock()
 
680
        return InterBranch.get(from_branch, self).fetch(last_revision)
698
681
 
699
682
    def get_bound_location(self):
700
683
        """Return the URL of the branch we are bound to.
711
694
 
712
695
    def get_commit_builder(self, parents, config=None, timestamp=None,
713
696
                           timezone=None, committer=None, revprops=None,
714
 
                           revision_id=None):
 
697
                           revision_id=None, lossy=False):
715
698
        """Obtain a CommitBuilder for this branch.
716
699
 
717
700
        :param parents: Revision ids of the parents of the new revision.
721
704
        :param committer: Optional committer to set for commit.
722
705
        :param revprops: Optional dictionary of revision properties.
723
706
        :param revision_id: Optional revision id.
 
707
        :param lossy: Whether to discard data that can not be natively
 
708
            represented, when pushing to a foreign VCS 
724
709
        """
725
710
 
726
711
        if config is None:
727
712
            config = self.get_config()
728
713
 
729
714
        return self.repository.get_commit_builder(self, parents, config,
730
 
            timestamp, timezone, committer, revprops, revision_id)
 
715
            timestamp, timezone, committer, revprops, revision_id,
 
716
            lossy)
731
717
 
732
718
    def get_master_branch(self, possible_transports=None):
733
719
        """Return the branch we are bound to.
937
923
        self._revision_history_cache = None
938
924
        self._revision_id_to_revno_cache = None
939
925
        self._last_revision_info_cache = None
 
926
        self._master_branch_cache = None
940
927
        self._merge_sorted_revisions_cache = None
941
928
        self._partial_revision_history_cache = []
942
929
        self._partial_revision_id_to_revno_cache = {}
1008
995
            return (0, _mod_revision.NULL_REVISION)
1009
996
 
1010
997
    def update_revisions(self, other, stop_revision=None, overwrite=False,
1011
 
                         graph=None, fetch_tags=True):
 
998
            graph=None):
1012
999
        """Pull in new perfect-fit revisions.
1013
1000
 
1014
1001
        :param other: Another Branch to pull from
1017
1004
            to see if it is a proper descendant.
1018
1005
        :param graph: A Graph object that can be used to query history
1019
1006
            information. This can be None.
1020
 
        :param fetch_tags: Flag that specifies if tags from other should be
1021
 
            fetched too.
1022
1007
        :return: None
1023
1008
        """
1024
1009
        return InterBranch.get(other, self).update_revisions(stop_revision,
1025
 
            overwrite, graph, fetch_tags=fetch_tags)
 
1010
            overwrite, graph)
1026
1011
 
1027
1012
    @deprecated_method(deprecated_in((2, 4, 0)))
1028
1013
    def import_last_revision_info(self, source_repo, revno, revid):
1036
1021
            self.repository.fetch(source_repo, revision_id=revid)
1037
1022
        self.set_last_revision_info(revno, revid)
1038
1023
 
1039
 
    def import_last_revision_info_and_tags(self, source, revno, revid):
 
1024
    def import_last_revision_info_and_tags(self, source, revno, revid,
 
1025
                                           lossy=False):
1040
1026
        """Set the last revision info, importing from another repo if necessary.
1041
1027
 
1042
1028
        This is used by the bound branch code to upload a revision to
1046
1032
        :param source: Source branch to optionally fetch from
1047
1033
        :param revno: Revision number of the new tip
1048
1034
        :param revid: Revision id of the new tip
 
1035
        :param lossy: Whether to discard metadata that can not be
 
1036
            natively represented
 
1037
        :return: Tuple with the new revision number and revision id
 
1038
            (should only be different from the arguments when lossy=True)
1049
1039
        """
1050
1040
        if not self.repository.has_same_location(source.repository):
1051
 
            try:
1052
 
                tags_to_fetch = set(source.tags.get_reverse_tag_dict())
1053
 
            except errors.TagsNotSupported:
1054
 
                tags_to_fetch = set()
1055
 
            fetch_spec = _mod_graph.NotInOtherForRevs(self.repository,
1056
 
                source.repository, [revid],
1057
 
                if_present_ids=tags_to_fetch).execute()
1058
 
            self.repository.fetch(source.repository, fetch_spec=fetch_spec)
 
1041
            self.fetch(source, revid)
1059
1042
        self.set_last_revision_info(revno, revid)
 
1043
        return (revno, revid)
1060
1044
 
1061
1045
    def revision_id_to_revno(self, revision_id):
1062
1046
        """Given a revision id, return its revno"""
1655
1639
        for hook in hooks:
1656
1640
            hook(params)
1657
1641
 
1658
 
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1659
 
                           repository=None):
1660
 
        """Initialize a branch in a bzrdir, with specified files
1661
 
 
1662
 
        :param a_bzrdir: The bzrdir to initialize the branch in
1663
 
        :param utf8_files: The files to create as a list of
1664
 
            (filename, content) tuples
1665
 
        :param name: Name of colocated branch to create, if any
1666
 
        :return: a branch in this format
1667
 
        """
1668
 
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1669
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1670
 
        control_files = lockable_files.LockableFiles(branch_transport,
1671
 
            'lock', lockdir.LockDir)
1672
 
        control_files.create_lock()
1673
 
        control_files.lock_write()
1674
 
        try:
1675
 
            utf8_files += [('format', self.get_format_string())]
1676
 
            for (filename, content) in utf8_files:
1677
 
                branch_transport.put_bytes(
1678
 
                    filename, content,
1679
 
                    mode=a_bzrdir._get_file_mode())
1680
 
        finally:
1681
 
            control_files.unlock()
1682
 
        branch = self.open(a_bzrdir, name, _found=True,
1683
 
                found_repository=repository)
1684
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1685
 
        return branch
1686
 
 
1687
1642
    def initialize(self, a_bzrdir, name=None, repository=None):
1688
1643
        """Create a branch of this format in a_bzrdir.
1689
1644
        
1818
1773
        These are all empty initially, because by default nothing should get
1819
1774
        notified.
1820
1775
        """
1821
 
        Hooks.__init__(self)
1822
 
        self.create_hook(HookPoint('set_rh',
 
1776
        Hooks.__init__(self, "bzrlib.branch", "Branch.hooks")
 
1777
        self.add_hook('set_rh',
1823
1778
            "Invoked whenever the revision history has been set via "
1824
1779
            "set_revision_history. The api signature is (branch, "
1825
1780
            "revision_history), and the branch will be write-locked. "
1826
1781
            "The set_rh hook can be expensive for bzr to trigger, a better "
1827
 
            "hook to use is Branch.post_change_branch_tip.", (0, 15), None))
1828
 
        self.create_hook(HookPoint('open',
 
1782
            "hook to use is Branch.post_change_branch_tip.", (0, 15))
 
1783
        self.add_hook('open',
1829
1784
            "Called with the Branch object that has been opened after a "
1830
 
            "branch is opened.", (1, 8), None))
1831
 
        self.create_hook(HookPoint('post_push',
 
1785
            "branch is opened.", (1, 8))
 
1786
        self.add_hook('post_push',
1832
1787
            "Called after a push operation completes. post_push is called "
1833
1788
            "with a bzrlib.branch.BranchPushResult object and only runs in the "
1834
 
            "bzr client.", (0, 15), None))
1835
 
        self.create_hook(HookPoint('post_pull',
 
1789
            "bzr client.", (0, 15))
 
1790
        self.add_hook('post_pull',
1836
1791
            "Called after a pull operation completes. post_pull is called "
1837
1792
            "with a bzrlib.branch.PullResult object and only runs in the "
1838
 
            "bzr client.", (0, 15), None))
1839
 
        self.create_hook(HookPoint('pre_commit',
 
1793
            "bzr client.", (0, 15))
 
1794
        self.add_hook('pre_commit',
1840
1795
            "Called after a commit is calculated but before it is "
1841
1796
            "completed. pre_commit is called with (local, master, old_revno, "
1842
1797
            "old_revid, future_revno, future_revid, tree_delta, future_tree"
1845
1800
            "basis revision. hooks MUST NOT modify this delta. "
1846
1801
            " future_tree is an in-memory tree obtained from "
1847
1802
            "CommitBuilder.revision_tree() and hooks MUST NOT modify this "
1848
 
            "tree.", (0,91), None))
1849
 
        self.create_hook(HookPoint('post_commit',
 
1803
            "tree.", (0,91))
 
1804
        self.add_hook('post_commit',
1850
1805
            "Called in the bzr client after a commit has completed. "
1851
1806
            "post_commit is called with (local, master, old_revno, old_revid, "
1852
1807
            "new_revno, new_revid). old_revid is NULL_REVISION for the first "
1853
 
            "commit to a branch.", (0, 15), None))
1854
 
        self.create_hook(HookPoint('post_uncommit',
 
1808
            "commit to a branch.", (0, 15))
 
1809
        self.add_hook('post_uncommit',
1855
1810
            "Called in the bzr client after an uncommit completes. "
1856
1811
            "post_uncommit is called with (local, master, old_revno, "
1857
1812
            "old_revid, new_revno, new_revid) where local is the local branch "
1858
1813
            "or None, master is the target branch, and an empty branch "
1859
 
            "receives new_revno of 0, new_revid of None.", (0, 15), None))
1860
 
        self.create_hook(HookPoint('pre_change_branch_tip',
 
1814
            "receives new_revno of 0, new_revid of None.", (0, 15))
 
1815
        self.add_hook('pre_change_branch_tip',
1861
1816
            "Called in bzr client and server before a change to the tip of a "
1862
1817
            "branch is made. pre_change_branch_tip is called with a "
1863
1818
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1864
 
            "commit, uncommit will all trigger this hook.", (1, 6), None))
1865
 
        self.create_hook(HookPoint('post_change_branch_tip',
 
1819
            "commit, uncommit will all trigger this hook.", (1, 6))
 
1820
        self.add_hook('post_change_branch_tip',
1866
1821
            "Called in bzr client and server after a change to the tip of a "
1867
1822
            "branch is made. post_change_branch_tip is called with a "
1868
1823
            "bzrlib.branch.ChangeBranchTipParams. Note that push, pull, "
1869
 
            "commit, uncommit will all trigger this hook.", (1, 4), None))
1870
 
        self.create_hook(HookPoint('transform_fallback_location',
 
1824
            "commit, uncommit will all trigger this hook.", (1, 4))
 
1825
        self.add_hook('transform_fallback_location',
1871
1826
            "Called when a stacked branch is activating its fallback "
1872
1827
            "locations. transform_fallback_location is called with (branch, "
1873
1828
            "url), and should return a new url. Returning the same url "
1878
1833
            "fallback locations have not been activated. When there are "
1879
1834
            "multiple hooks installed for transform_fallback_location, "
1880
1835
            "all are called with the url returned from the previous hook."
1881
 
            "The order is however undefined.", (1, 9), None))
1882
 
        self.create_hook(HookPoint('automatic_tag_name',
 
1836
            "The order is however undefined.", (1, 9))
 
1837
        self.add_hook('automatic_tag_name',
1883
1838
            "Called to determine an automatic tag name for a revision. "
1884
1839
            "automatic_tag_name is called with (branch, revision_id) and "
1885
1840
            "should return a tag name or None if no tag name could be "
1886
1841
            "determined. The first non-None tag name returned will be used.",
1887
 
            (2, 2), None))
1888
 
        self.create_hook(HookPoint('post_branch_init',
 
1842
            (2, 2))
 
1843
        self.add_hook('post_branch_init',
1889
1844
            "Called after new branch initialization completes. "
1890
1845
            "post_branch_init is called with a "
1891
1846
            "bzrlib.branch.BranchInitHookParams. "
1892
1847
            "Note that init, branch and checkout (both heavyweight and "
1893
 
            "lightweight) will all trigger this hook.", (2, 2), None))
1894
 
        self.create_hook(HookPoint('post_switch',
 
1848
            "lightweight) will all trigger this hook.", (2, 2))
 
1849
        self.add_hook('post_switch',
1895
1850
            "Called after a checkout switches branch. "
1896
1851
            "post_switch is called with a "
1897
 
            "bzrlib.branch.SwitchHookParams.", (2, 2), None))
 
1852
            "bzrlib.branch.SwitchHookParams.", (2, 2))
1898
1853
 
1899
1854
 
1900
1855
 
2020
1975
        """What class to instantiate on open calls."""
2021
1976
        raise NotImplementedError(self._branch_class)
2022
1977
 
 
1978
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
 
1979
                           repository=None):
 
1980
        """Initialize a branch in a bzrdir, with specified files
 
1981
 
 
1982
        :param a_bzrdir: The bzrdir to initialize the branch in
 
1983
        :param utf8_files: The files to create as a list of
 
1984
            (filename, content) tuples
 
1985
        :param name: Name of colocated branch to create, if any
 
1986
        :return: a branch in this format
 
1987
        """
 
1988
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
 
1989
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
 
1990
        control_files = lockable_files.LockableFiles(branch_transport,
 
1991
            'lock', lockdir.LockDir)
 
1992
        control_files.create_lock()
 
1993
        control_files.lock_write()
 
1994
        try:
 
1995
            utf8_files += [('format', self.get_format_string())]
 
1996
            for (filename, content) in utf8_files:
 
1997
                branch_transport.put_bytes(
 
1998
                    filename, content,
 
1999
                    mode=a_bzrdir._get_file_mode())
 
2000
        finally:
 
2001
            control_files.unlock()
 
2002
        branch = self.open(a_bzrdir, name, _found=True,
 
2003
                found_repository=repository)
 
2004
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
 
2005
        return branch
 
2006
 
2023
2007
    def network_name(self):
2024
2008
        """A simple byte string uniquely identifying this format for RPC calls.
2025
2009
 
2158
2142
                      ]
2159
2143
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2160
2144
 
2161
 
    def __init__(self):
2162
 
        super(BzrBranchFormat8, self).__init__()
2163
 
        self._matchingbzrdir.repository_format = \
2164
 
            RepositoryFormatKnitPack5RichRoot()
2165
 
 
2166
2145
    def make_tags(self, branch):
2167
2146
        """See bzrlib.branch.BranchFormat.make_tags()."""
2168
2147
        return BasicTags(branch)
2176
2155
    supports_reference_locations = True
2177
2156
 
2178
2157
 
2179
 
class BzrBranchFormat7(BzrBranchFormat8):
 
2158
class BzrBranchFormat7(BranchFormatMetadir):
2180
2159
    """Branch format with last-revision, tags, and a stacked location pointer.
2181
2160
 
2182
2161
    The stacked location pointer is passed down to the repository and requires
2207
2186
    def supports_set_append_revisions_only(self):
2208
2187
        return True
2209
2188
 
 
2189
    def supports_stacking(self):
 
2190
        return True
 
2191
 
 
2192
    def make_tags(self, branch):
 
2193
        """See bzrlib.branch.BranchFormat.make_tags()."""
 
2194
        return BasicTags(branch)
 
2195
 
2210
2196
    supports_reference_locations = False
2211
2197
 
2212
2198
 
2511
2497
            'revision-history', '\n'.join(history),
2512
2498
            mode=self.bzrdir._get_file_mode())
2513
2499
 
2514
 
    @needs_write_lock
 
2500
    @deprecated_method(deprecated_in((2, 4, 0)))
2515
2501
    def set_revision_history(self, rev_history):
2516
2502
        """See Branch.set_revision_history."""
 
2503
        self._set_revision_history(rev_history)
 
2504
 
 
2505
    @needs_write_lock
 
2506
    def _set_revision_history(self, rev_history):
2517
2507
        if 'evil' in debug.debug_flags:
2518
2508
            mutter_callsite(3, "set_revision_history scales with history.")
2519
2509
        check_not_reserved_id = _mod_revision.check_not_reserved_id
2563
2553
            except ValueError:
2564
2554
                rev = self.repository.get_revision(revision_id)
2565
2555
                new_history = rev.get_history(self.repository)[1:]
2566
 
        destination.set_revision_history(new_history)
 
2556
        destination._set_revision_history(new_history)
2567
2557
 
2568
2558
    @needs_write_lock
2569
2559
    def set_last_revision_info(self, revno, revision_id):
2577
2567
        configured to check constraints on history, in which case this may not
2578
2568
        be permitted.
2579
2569
        """
2580
 
        revision_id = _mod_revision.ensure_null(revision_id)
 
2570
        if not revision_id or not isinstance(revision_id, basestring):
 
2571
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2581
2572
        # this old format stores the full history, but this api doesn't
2582
2573
        # provide it, so we must generate, and might as well check it's
2583
2574
        # correct
2584
2575
        history = self._lefthand_history(revision_id)
2585
2576
        if len(history) != revno:
2586
2577
            raise AssertionError('%d != %d' % (len(history), revno))
2587
 
        self.set_revision_history(history)
 
2578
        self._set_revision_history(history)
2588
2579
 
2589
2580
    def _gen_revision_history(self):
2590
2581
        history = self._transport.get_bytes('revision-history').split('\n')
2604
2595
        :param other_branch: The other branch that DivergedBranches should
2605
2596
            raise with respect to.
2606
2597
        """
2607
 
        self.set_revision_history(self._lefthand_history(revision_id,
 
2598
        self._set_revision_history(self._lefthand_history(revision_id,
2608
2599
            last_rev, other_branch))
2609
2600
 
2610
2601
    def basis_tree(self):
2637
2628
            target.update_revisions(self, stop_revision,
2638
2629
                overwrite=overwrite, graph=graph)
2639
2630
        if self._push_should_merge_tags():
2640
 
            result.tag_conflicts = self.tags.merge_to(target.tags,
2641
 
                overwrite)
 
2631
            result.tag_conflicts = self.tags.merge_to(target.tags, overwrite)
2642
2632
        result.new_revno, result.new_revid = target.last_revision_info()
2643
2633
        return result
2644
2634
 
2676
2666
        """Return the branch we are bound to.
2677
2667
 
2678
2668
        :return: Either a Branch, or None
2679
 
 
2680
 
        This could memoise the branch, but if thats done
2681
 
        it must be revalidated on each new lock.
2682
 
        So for now we just don't memoise it.
2683
 
        # RBC 20060304 review this decision.
2684
2669
        """
 
2670
        if self._master_branch_cache is None:
 
2671
            self._master_branch_cache = self._get_master_branch(
 
2672
                possible_transports)
 
2673
        return self._master_branch_cache
 
2674
 
 
2675
    def _get_master_branch(self, possible_transports):
2685
2676
        bound_loc = self.get_bound_location()
2686
2677
        if not bound_loc:
2687
2678
            return None
2698
2689
 
2699
2690
        :param location: URL to the target branch
2700
2691
        """
 
2692
        self._master_branch_cache = None
2701
2693
        if location:
2702
2694
            self._transport.put_bytes('bound', location+'\n',
2703
2695
                mode=self.bzrdir._get_file_mode())
2812
2804
 
2813
2805
    @needs_write_lock
2814
2806
    def set_last_revision_info(self, revno, revision_id):
2815
 
        revision_id = _mod_revision.ensure_null(revision_id)
 
2807
        if not revision_id or not isinstance(revision_id, basestring):
 
2808
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2816
2809
        old_revno, old_revid = self.last_revision_info()
2817
2810
        if self._get_append_revisions_only():
2818
2811
            self._check_history_violation(revision_id)
2955
2948
 
2956
2949
    def set_bound_location(self, location):
2957
2950
        """See Branch.set_push_location."""
 
2951
        self._master_branch_cache = None
2958
2952
        result = None
2959
2953
        config = self.get_config()
2960
2954
        if location is None:
3294
3288
 
3295
3289
    @needs_write_lock
3296
3290
    def update_revisions(self, stop_revision=None, overwrite=False,
3297
 
                         graph=None, fetch_tags=True):
 
3291
            graph=None):
3298
3292
        """Pull in new perfect-fit revisions.
3299
3293
 
3300
3294
        :param stop_revision: Updated until the given revision
3302
3296
            to see if it is a proper descendant.
3303
3297
        :param graph: A Graph object that can be used to query history
3304
3298
            information. This can be None.
3305
 
        :param fetch_tags: Flag that specifies if tags from source should be
3306
 
            fetched too.
3307
3299
        :return: None
3308
3300
        """
3309
3301
        raise NotImplementedError(self.update_revisions)
3326
3318
        """
3327
3319
        raise NotImplementedError(self.copy_content_into)
3328
3320
 
 
3321
    @needs_write_lock
 
3322
    def fetch(self, stop_revision=None):
 
3323
        """Fetch revisions.
 
3324
 
 
3325
        :param stop_revision: Last revision to fetch
 
3326
        """
 
3327
        raise NotImplementedError(self.fetch)
 
3328
 
3329
3329
 
3330
3330
class GenericInterBranch(InterBranch):
3331
3331
    """InterBranch implementation that uses public Branch functions."""
3366
3366
            self.source.tags.merge_to(self.target.tags)
3367
3367
 
3368
3368
    @needs_write_lock
 
3369
    def fetch(self, stop_revision=None):
 
3370
        if self.target.base == self.source.base:
 
3371
            return (0, [])
 
3372
        self.source.lock_read()
 
3373
        try:
 
3374
            fetch_spec_factory = fetch.FetchSpecFactory()
 
3375
            fetch_spec_factory.source_branch = self.source
 
3376
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
 
3377
            fetch_spec_factory.source_repo = self.source.repository
 
3378
            fetch_spec_factory.target_repo = self.target.repository
 
3379
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
 
3380
            fetch_spec = fetch_spec_factory.make_fetch_spec()
 
3381
            return self.target.repository.fetch(self.source.repository,
 
3382
                fetch_spec=fetch_spec)
 
3383
        finally:
 
3384
            self.source.unlock()
 
3385
 
 
3386
    @needs_write_lock
3369
3387
    def update_revisions(self, stop_revision=None, overwrite=False,
3370
 
        graph=None, fetch_tags=True):
 
3388
            graph=None):
3371
3389
        """See InterBranch.update_revisions()."""
3372
3390
        other_revno, other_last_revision = self.source.last_revision_info()
3373
3391
        stop_revno = None # unknown
3385
3403
        # case of having something to pull, and so that the check for
3386
3404
        # already merged can operate on the just fetched graph, which will
3387
3405
        # be cached in memory.
3388
 
        if fetch_tags:
3389
 
            fetch_spec_factory = fetch.FetchSpecFactory()
3390
 
            fetch_spec_factory.source_branch = self.source
3391
 
            fetch_spec_factory.source_branch_stop_revision_id = stop_revision
3392
 
            fetch_spec_factory.source_repo = self.source.repository
3393
 
            fetch_spec_factory.target_repo = self.target.repository
3394
 
            fetch_spec_factory.target_repo_kind = fetch.TargetRepoKinds.PREEXISTING
3395
 
            fetch_spec = fetch_spec_factory.make_fetch_spec()
3396
 
        else:
3397
 
            fetch_spec = _mod_graph.NotInOtherForRevs(self.target.repository,
3398
 
                self.source.repository, revision_ids=[stop_revision]).execute()
3399
 
        self.target.fetch(self.source, fetch_spec=fetch_spec)
 
3406
        self.fetch(stop_revision=stop_revision)
3400
3407
        # Check to see if one is an ancestor of the other
3401
3408
        if not overwrite:
3402
3409
            if graph is None: