/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/branch.py

Merge trunk.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
from __future__ import absolute_import
18
18
 
19
 
import breezy.bzrdir
 
19
from . import errors
20
20
 
21
21
from .lazy_import import lazy_import
22
22
lazy_import(globals(), """
23
23
import itertools
24
24
from breezy import (
25
 
    bzrdir,
26
 
    controldir,
27
 
    cache_utf8,
28
25
    cleanup,
29
26
    config as _mod_config,
30
27
    debug,
31
 
    errors,
32
28
    fetch,
33
 
    graph as _mod_graph,
34
 
    lockdir,
35
 
    lockable_files,
36
29
    remote,
37
30
    repository,
38
31
    revision as _mod_revision,
39
 
    rio,
40
 
    shelf,
41
32
    tag as _mod_tag,
42
33
    transport,
43
34
    ui,
47
38
from breezy.i18n import gettext, ngettext
48
39
""")
49
40
 
50
 
# Explicitly import breezy.bzrdir so that the BzrProber
51
 
# is guaranteed to be registered.
52
 
import breezy.bzrdir
53
 
 
54
41
from . import (
55
 
    bzrdir,
56
42
    controldir,
57
43
    registry,
58
44
    )
63
49
    )
64
50
from .hooks import Hooks
65
51
from .inter import InterObject
66
 
from .lock import _RelockDebugMixin, LogicalLockResult
 
52
from .lock import LogicalLockResult
67
53
from .sixish import (
68
54
    BytesIO,
69
55
    viewitems,
116
102
        for existing_fallback_repo in self.repository._fallback_repositories:
117
103
            if existing_fallback_repo.user_url == url:
118
104
                # This fallback is already configured.  This probably only
119
 
                # happens because ControlDir.sprout is a horrible mess.  To avoid
120
 
                # confusing _unstack we don't add this a second time.
 
105
                # happens because ControlDir.sprout is a horrible mess.  To
 
106
                # avoid confusing _unstack we don't add this a second time.
121
107
                mutter('duplicate activation of fallback %r on %r', url, self)
122
108
                return
123
109
        repo = self._get_fallback_repository(url, possible_transports)
141
127
 
142
128
    def _check_stackable_repo(self):
143
129
        if not self.repository._format.supports_external_lookups:
144
 
            raise errors.UnstackableRepositoryFormat(self.repository._format,
145
 
                self.repository.base)
 
130
            raise errors.UnstackableRepositoryFormat(
 
131
                self.repository._format, self.repository.base)
146
132
 
147
133
    def _extend_partial_history(self, stop_index=None, stop_revision=None):
148
134
        """Extend the partial history to include a given index
1700
1686
        return True
1701
1687
 
1702
1688
 
1703
 
class MetaDirBranchFormatFactory(registry._LazyObjectGetter):
1704
 
    """A factory for a BranchFormat object, permitting simple lazy registration.
1705
 
    
1706
 
    While none of the built in BranchFormats are lazy registered yet,
1707
 
    breezy.tests.test_branch.TestMetaDirBranchFormatFactory demonstrates how to
1708
 
    use it, and the bzr-loom plugin uses it as well (see
1709
 
    breezy.plugins.loom.formats).
1710
 
    """
1711
 
 
1712
 
    def __init__(self, format_string, module_name, member_name):
1713
 
        """Create a MetaDirBranchFormatFactory.
1714
 
 
1715
 
        :param format_string: The format string the format has.
1716
 
        :param module_name: Module to load the format class from.
1717
 
        :param member_name: Attribute name within the module for the format class.
1718
 
        """
1719
 
        registry._LazyObjectGetter.__init__(self, module_name, member_name)
1720
 
        self._format_string = format_string
1721
 
 
1722
 
    def get_format_string(self):
1723
 
        """See BranchFormat.get_format_string."""
1724
 
        return self._format_string
1725
 
 
1726
 
    def __call__(self):
1727
 
        """Used for network_format_registry support."""
1728
 
        return self.get_obj()()
1729
 
 
1730
 
 
1731
1689
class BranchHooks(Hooks):
1732
1690
    """A dictionary mapping hook name to a list of callables for branch hooks.
1733
1691
 
1930
1888
            self.revision_id)
1931
1889
 
1932
1890
 
1933
 
class BranchFormatMetadir(bzrdir.BzrFormat, BranchFormat):
1934
 
    """Base class for branch formats that live in meta directories.
1935
 
    """
1936
 
 
1937
 
    def __init__(self):
1938
 
        BranchFormat.__init__(self)
1939
 
        bzrdir.BzrFormat.__init__(self)
1940
 
 
1941
 
    @classmethod
1942
 
    def find_format(klass, controldir, name=None):
1943
 
        """Return the format for the branch object in controldir."""
1944
 
        try:
1945
 
            transport = controldir.get_branch_transport(None, name=name)
1946
 
        except errors.NoSuchFile:
1947
 
            raise errors.NotBranchError(path=name, bzrdir=controldir)
1948
 
        try:
1949
 
            format_string = transport.get_bytes("format")
1950
 
        except errors.NoSuchFile:
1951
 
            raise errors.NotBranchError(path=transport.base, bzrdir=controldir)
1952
 
        return klass._find_format(format_registry, 'branch', format_string)
1953
 
 
1954
 
    def _branch_class(self):
1955
 
        """What class to instantiate on open calls."""
1956
 
        raise NotImplementedError(self._branch_class)
1957
 
 
1958
 
    def _get_initial_config(self, append_revisions_only=None):
1959
 
        if append_revisions_only:
1960
 
            return "append_revisions_only = True\n"
1961
 
        else:
1962
 
            # Avoid writing anything if append_revisions_only is disabled,
1963
 
            # as that is the default.
1964
 
            return ""
1965
 
 
1966
 
    def _initialize_helper(self, a_bzrdir, utf8_files, name=None,
1967
 
                           repository=None):
1968
 
        """Initialize a branch in a control dir, with specified files
1969
 
 
1970
 
        :param a_bzrdir: The bzrdir to initialize the branch in
1971
 
        :param utf8_files: The files to create as a list of
1972
 
            (filename, content) tuples
1973
 
        :param name: Name of colocated branch to create, if any
1974
 
        :return: a branch in this format
1975
 
        """
1976
 
        if name is None:
1977
 
            name = a_bzrdir._get_selected_branch()
1978
 
        mutter('creating branch %r in %s', self, a_bzrdir.user_url)
1979
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
1980
 
        control_files = lockable_files.LockableFiles(branch_transport,
1981
 
            'lock', lockdir.LockDir)
1982
 
        control_files.create_lock()
1983
 
        control_files.lock_write()
1984
 
        try:
1985
 
            utf8_files += [('format', self.as_string())]
1986
 
            for (filename, content) in utf8_files:
1987
 
                branch_transport.put_bytes(
1988
 
                    filename, content,
1989
 
                    mode=a_bzrdir._get_file_mode())
1990
 
        finally:
1991
 
            control_files.unlock()
1992
 
        branch = self.open(a_bzrdir, name, _found=True,
1993
 
                found_repository=repository)
1994
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
1995
 
        return branch
1996
 
 
1997
 
    def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False,
1998
 
            found_repository=None, possible_transports=None):
1999
 
        """See BranchFormat.open()."""
2000
 
        if name is None:
2001
 
            name = a_bzrdir._get_selected_branch()
2002
 
        if not _found:
2003
 
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2004
 
            if format.__class__ != self.__class__:
2005
 
                raise AssertionError("wrong format %r found for %r" %
2006
 
                    (format, self))
2007
 
        transport = a_bzrdir.get_branch_transport(None, name=name)
2008
 
        try:
2009
 
            control_files = lockable_files.LockableFiles(transport, 'lock',
2010
 
                                                         lockdir.LockDir)
2011
 
            if found_repository is None:
2012
 
                found_repository = a_bzrdir.find_repository()
2013
 
            return self._branch_class()(_format=self,
2014
 
                              _control_files=control_files,
2015
 
                              name=name,
2016
 
                              a_bzrdir=a_bzrdir,
2017
 
                              _repository=found_repository,
2018
 
                              ignore_fallbacks=ignore_fallbacks,
2019
 
                              possible_transports=possible_transports)
2020
 
        except errors.NoSuchFile:
2021
 
            raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir)
2022
 
 
2023
 
    @property
2024
 
    def _matchingbzrdir(self):
2025
 
        ret = bzrdir.BzrDirMetaFormat1()
2026
 
        ret.set_branch_format(self)
2027
 
        return ret
2028
 
 
2029
 
    def supports_tags(self):
2030
 
        return True
2031
 
 
2032
 
    def supports_leaving_lock(self):
2033
 
        return True
2034
 
 
2035
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
2036
 
            basedir=None):
2037
 
        BranchFormat.check_support_status(self,
2038
 
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
2039
 
            basedir=basedir)
2040
 
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
2041
 
            recommend_upgrade=recommend_upgrade, basedir=basedir)
2042
 
 
2043
 
 
2044
 
class BzrBranchFormat6(BranchFormatMetadir):
2045
 
    """Branch format with last-revision and tags.
2046
 
 
2047
 
    Unlike previous formats, this has no explicit revision history. Instead,
2048
 
    this just stores the last-revision, and the left-hand history leading
2049
 
    up to there is the history.
2050
 
 
2051
 
    This format was introduced in bzr 0.15
2052
 
    and became the default in 0.91.
2053
 
    """
2054
 
 
2055
 
    def _branch_class(self):
2056
 
        return BzrBranch6
2057
 
 
2058
 
    @classmethod
2059
 
    def get_format_string(cls):
2060
 
        """See BranchFormat.get_format_string()."""
2061
 
        return "Bazaar Branch Format 6 (bzr 0.15)\n"
2062
 
 
2063
 
    def get_format_description(self):
2064
 
        """See BranchFormat.get_format_description()."""
2065
 
        return "Branch format 6"
2066
 
 
2067
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2068
 
                   append_revisions_only=None):
2069
 
        """Create a branch of this format in a_bzrdir."""
2070
 
        utf8_files = [('last-revision', '0 null:\n'),
2071
 
                      ('branch.conf',
2072
 
                          self._get_initial_config(append_revisions_only)),
2073
 
                      ('tags', ''),
2074
 
                      ]
2075
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2076
 
 
2077
 
    def make_tags(self, branch):
2078
 
        """See breezy.branch.BranchFormat.make_tags()."""
2079
 
        return _mod_tag.BasicTags(branch)
2080
 
 
2081
 
    def supports_set_append_revisions_only(self):
2082
 
        return True
2083
 
 
2084
 
 
2085
 
class BzrBranchFormat8(BranchFormatMetadir):
2086
 
    """Metadir format supporting storing locations of subtree branches."""
2087
 
 
2088
 
    def _branch_class(self):
2089
 
        return BzrBranch8
2090
 
 
2091
 
    @classmethod
2092
 
    def get_format_string(cls):
2093
 
        """See BranchFormat.get_format_string()."""
2094
 
        return "Bazaar Branch Format 8 (needs bzr 1.15)\n"
2095
 
 
2096
 
    def get_format_description(self):
2097
 
        """See BranchFormat.get_format_description()."""
2098
 
        return "Branch format 8"
2099
 
 
2100
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2101
 
                   append_revisions_only=None):
2102
 
        """Create a branch of this format in a_bzrdir."""
2103
 
        utf8_files = [('last-revision', '0 null:\n'),
2104
 
                      ('branch.conf',
2105
 
                          self._get_initial_config(append_revisions_only)),
2106
 
                      ('tags', ''),
2107
 
                      ('references', '')
2108
 
                      ]
2109
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2110
 
 
2111
 
    def make_tags(self, branch):
2112
 
        """See breezy.branch.BranchFormat.make_tags()."""
2113
 
        return _mod_tag.BasicTags(branch)
2114
 
 
2115
 
    def supports_set_append_revisions_only(self):
2116
 
        return True
2117
 
 
2118
 
    def supports_stacking(self):
2119
 
        return True
2120
 
 
2121
 
    supports_reference_locations = True
2122
 
 
2123
 
 
2124
 
class BzrBranchFormat7(BranchFormatMetadir):
2125
 
    """Branch format with last-revision, tags, and a stacked location pointer.
2126
 
 
2127
 
    The stacked location pointer is passed down to the repository and requires
2128
 
    a repository format with supports_external_lookups = True.
2129
 
 
2130
 
    This format was introduced in bzr 1.6.
2131
 
    """
2132
 
 
2133
 
    def initialize(self, a_bzrdir, name=None, repository=None,
2134
 
                   append_revisions_only=None):
2135
 
        """Create a branch of this format in a_bzrdir."""
2136
 
        utf8_files = [('last-revision', '0 null:\n'),
2137
 
                      ('branch.conf',
2138
 
                          self._get_initial_config(append_revisions_only)),
2139
 
                      ('tags', ''),
2140
 
                      ]
2141
 
        return self._initialize_helper(a_bzrdir, utf8_files, name, repository)
2142
 
 
2143
 
    def _branch_class(self):
2144
 
        return BzrBranch7
2145
 
 
2146
 
    @classmethod
2147
 
    def get_format_string(cls):
2148
 
        """See BranchFormat.get_format_string()."""
2149
 
        return "Bazaar Branch Format 7 (needs bzr 1.6)\n"
2150
 
 
2151
 
    def get_format_description(self):
2152
 
        """See BranchFormat.get_format_description()."""
2153
 
        return "Branch format 7"
2154
 
 
2155
 
    def supports_set_append_revisions_only(self):
2156
 
        return True
2157
 
 
2158
 
    def supports_stacking(self):
2159
 
        return True
2160
 
 
2161
 
    def make_tags(self, branch):
2162
 
        """See breezy.branch.BranchFormat.make_tags()."""
2163
 
        return _mod_tag.BasicTags(branch)
2164
 
 
2165
 
    supports_reference_locations = False
2166
 
 
2167
 
 
2168
 
class BranchReferenceFormat(BranchFormatMetadir):
2169
 
    """Bzr branch reference format.
2170
 
 
2171
 
    Branch references are used in implementing checkouts, they
2172
 
    act as an alias to the real branch which is at some other url.
2173
 
 
2174
 
    This format has:
2175
 
     - A location file
2176
 
     - a format string
2177
 
    """
2178
 
 
2179
 
    @classmethod
2180
 
    def get_format_string(cls):
2181
 
        """See BranchFormat.get_format_string()."""
2182
 
        return "Bazaar-NG Branch Reference Format 1\n"
2183
 
 
2184
 
    def get_format_description(self):
2185
 
        """See BranchFormat.get_format_description()."""
2186
 
        return "Checkout reference format 1"
2187
 
 
2188
 
    def get_reference(self, a_bzrdir, name=None):
2189
 
        """See BranchFormat.get_reference()."""
2190
 
        transport = a_bzrdir.get_branch_transport(None, name=name)
2191
 
        return transport.get_bytes('location')
2192
 
 
2193
 
    def set_reference(self, a_bzrdir, name, to_branch):
2194
 
        """See BranchFormat.set_reference()."""
2195
 
        transport = a_bzrdir.get_branch_transport(None, name=name)
2196
 
        location = transport.put_bytes('location', to_branch.base)
2197
 
 
2198
 
    def initialize(self, a_bzrdir, name=None, target_branch=None,
2199
 
            repository=None, append_revisions_only=None):
2200
 
        """Create a branch of this format in a_bzrdir."""
2201
 
        if target_branch is None:
2202
 
            # this format does not implement branch itself, thus the implicit
2203
 
            # creation contract must see it as uninitializable
2204
 
            raise errors.UninitializableFormat(self)
2205
 
        mutter('creating branch reference in %s', a_bzrdir.user_url)
2206
 
        if a_bzrdir._format.fixed_components:
2207
 
            raise errors.IncompatibleFormat(self, a_bzrdir._format)
2208
 
        if name is None:
2209
 
            name = a_bzrdir._get_selected_branch()
2210
 
        branch_transport = a_bzrdir.get_branch_transport(self, name=name)
2211
 
        branch_transport.put_bytes('location',
2212
 
            target_branch.user_url)
2213
 
        branch_transport.put_bytes('format', self.as_string())
2214
 
        branch = self.open(a_bzrdir, name, _found=True,
2215
 
            possible_transports=[target_branch.bzrdir.root_transport])
2216
 
        self._run_post_branch_init_hooks(a_bzrdir, name, branch)
2217
 
        return branch
2218
 
 
2219
 
    def _make_reference_clone_function(format, a_branch):
2220
 
        """Create a clone() routine for a branch dynamically."""
2221
 
        def clone(to_bzrdir, revision_id=None,
2222
 
            repository_policy=None):
2223
 
            """See Branch.clone()."""
2224
 
            return format.initialize(to_bzrdir, target_branch=a_branch)
2225
 
            # cannot obey revision_id limits when cloning a reference ...
2226
 
            # FIXME RBC 20060210 either nuke revision_id for clone, or
2227
 
            # emit some sort of warning/error to the caller ?!
2228
 
        return clone
2229
 
 
2230
 
    def open(self, a_bzrdir, name=None, _found=False, location=None,
2231
 
             possible_transports=None, ignore_fallbacks=False,
2232
 
             found_repository=None):
2233
 
        """Return the branch that the branch reference in a_bzrdir points at.
2234
 
 
2235
 
        :param a_bzrdir: A BzrDir that contains a branch.
2236
 
        :param name: Name of colocated branch to open, if any
2237
 
        :param _found: a private parameter, do not use it. It is used to
2238
 
            indicate if format probing has already be done.
2239
 
        :param ignore_fallbacks: when set, no fallback branches will be opened
2240
 
            (if there are any).  Default is to open fallbacks.
2241
 
        :param location: The location of the referenced branch.  If
2242
 
            unspecified, this will be determined from the branch reference in
2243
 
            a_bzrdir.
2244
 
        :param possible_transports: An optional reusable transports list.
2245
 
        """
2246
 
        if name is None:
2247
 
            name = a_bzrdir._get_selected_branch()
2248
 
        if not _found:
2249
 
            format = BranchFormatMetadir.find_format(a_bzrdir, name=name)
2250
 
            if format.__class__ != self.__class__:
2251
 
                raise AssertionError("wrong format %r found for %r" %
2252
 
                    (format, self))
2253
 
        if location is None:
2254
 
            location = self.get_reference(a_bzrdir, name)
2255
 
        real_bzrdir = controldir.ControlDir.open(
2256
 
            location, possible_transports=possible_transports)
2257
 
        result = real_bzrdir.open_branch(ignore_fallbacks=ignore_fallbacks,
2258
 
            possible_transports=possible_transports)
2259
 
        # this changes the behaviour of result.clone to create a new reference
2260
 
        # rather than a copy of the content of the branch.
2261
 
        # I did not use a proxy object because that needs much more extensive
2262
 
        # testing, and we are only changing one behaviour at the moment.
2263
 
        # If we decide to alter more behaviours - i.e. the implicit nickname
2264
 
        # then this should be refactored to introduce a tested proxy branch
2265
 
        # and a subclass of that for use in overriding clone() and ....
2266
 
        # - RBC 20060210
2267
 
        result.clone = self._make_reference_clone_function(result)
2268
 
        return result
2269
 
 
2270
 
 
2271
1891
class BranchFormatRegistry(controldir.ControlComponentFormatRegistry):
2272
1892
    """Branch format registry."""
2273
1893
 
2274
1894
    def __init__(self, other_registry=None):
2275
1895
        super(BranchFormatRegistry, self).__init__(other_registry)
2276
1896
        self._default_format = None
 
1897
        self._default_format_key = None
 
1898
 
 
1899
    def get_default(self):
 
1900
        """Return the current default format."""
 
1901
        if (self._default_format_key is not None and
 
1902
            self._default_format is None):
 
1903
            self._default_format = self.get(self._default_format_key)
 
1904
        return self._default_format
2277
1905
 
2278
1906
    def set_default(self, format):
 
1907
        """Set the default format."""
2279
1908
        self._default_format = format
 
1909
        self._default_format_key = None
2280
1910
 
2281
 
    def get_default(self):
2282
 
        return self._default_format
 
1911
    def set_default_key(self, format_string):
 
1912
        """Set the default format by its format string."""
 
1913
        self._default_format_key = format_string
 
1914
        self._default_format = None
2283
1915
 
2284
1916
 
2285
1917
network_format_registry = registry.FormatRegistry()
2295
1927
 
2296
1928
# formats which have no format string are not discoverable
2297
1929
# and not independently creatable, so are not registered.
2298
 
__format6 = BzrBranchFormat6()
2299
 
__format7 = BzrBranchFormat7()
2300
 
__format8 = BzrBranchFormat8()
2301
 
format_registry.register_lazy(
2302
 
    "Bazaar-NG branch format 5\n", "breezy.branchfmt.fullhistory", "BzrBranchFormat5")
2303
 
format_registry.register(BranchReferenceFormat())
2304
 
format_registry.register(__format6)
2305
 
format_registry.register(__format7)
2306
 
format_registry.register(__format8)
2307
 
format_registry.set_default(__format7)
 
1930
format_registry.register_lazy(
 
1931
    "Bazaar-NG branch format 5\n", "breezy.branchfmt.fullhistory",
 
1932
    "BzrBranchFormat5")
 
1933
format_registry.register_lazy(
 
1934
    "Bazaar Branch Format 6 (bzr 0.15)\n",
 
1935
    "breezy.bzrbranch", "BzrBranchFormat6")
 
1936
format_registry.register_lazy(
 
1937
    "Bazaar Branch Format 7 (needs bzr 1.6)\n",
 
1938
    "breezy.bzrbranch", "BzrBranchFormat7")
 
1939
format_registry.register_lazy(
 
1940
    "Bazaar Branch Format 8 (needs bzr 1.15)\n",
 
1941
    "breezy.bzrbranch", "BzrBranchFormat8")
 
1942
format_registry.register_lazy(
 
1943
    "Bazaar-NG Branch Reference Format 1\n",
 
1944
    "breezy.bzrbranch", "BranchReferenceFormat")
 
1945
 
 
1946
format_registry.set_default_key("Bazaar Branch Format 7 (needs bzr 1.6)\n")
2308
1947
 
2309
1948
 
2310
1949
class BranchWriteLockResult(LogicalLockResult):
2324
1963
            self.unlock)
2325
1964
 
2326
1965
 
2327
 
class BzrBranch(Branch, _RelockDebugMixin):
2328
 
    """A branch stored in the actual filesystem.
2329
 
 
2330
 
    Note that it's "local" in the context of the filesystem; it doesn't
2331
 
    really matter if it's on an nfs/smb/afs/coda/... share, as long as
2332
 
    it's writable, and can be accessed via the normal filesystem API.
2333
 
 
2334
 
    :ivar _transport: Transport for file operations on this branch's
2335
 
        control files, typically pointing to the .bzr/branch directory.
2336
 
    :ivar repository: Repository for this branch.
2337
 
    :ivar base: The url of the base directory for this branch; the one
2338
 
        containing the .bzr directory.
2339
 
    :ivar name: Optional colocated branch name as it exists in the control
2340
 
        directory.
2341
 
    """
2342
 
 
2343
 
    def __init__(self, _format=None,
2344
 
                 _control_files=None, a_bzrdir=None, name=None,
2345
 
                 _repository=None, ignore_fallbacks=False,
2346
 
                 possible_transports=None):
2347
 
        """Create new branch object at a particular location."""
2348
 
        if a_bzrdir is None:
2349
 
            raise ValueError('a_bzrdir must be supplied')
2350
 
        if name is None:
2351
 
            raise ValueError('name must be supplied')
2352
 
        self.bzrdir = a_bzrdir
2353
 
        self._user_transport = self.bzrdir.transport.clone('..')
2354
 
        if name != "":
2355
 
            self._user_transport.set_segment_parameter(
2356
 
                "branch", urlutils.escape(name))
2357
 
        self._base = self._user_transport.base
2358
 
        self.name = name
2359
 
        self._format = _format
2360
 
        if _control_files is None:
2361
 
            raise ValueError('BzrBranch _control_files is None')
2362
 
        self.control_files = _control_files
2363
 
        self._transport = _control_files._transport
2364
 
        self.repository = _repository
2365
 
        self.conf_store = None
2366
 
        Branch.__init__(self, possible_transports)
2367
 
 
2368
 
    def __str__(self):
2369
 
        return '%s(%s)' % (self.__class__.__name__, self.user_url)
2370
 
 
2371
 
    __repr__ = __str__
2372
 
 
2373
 
    def _get_base(self):
2374
 
        """Returns the directory containing the control directory."""
2375
 
        return self._base
2376
 
 
2377
 
    base = property(_get_base, doc="The URL for the root of this branch.")
2378
 
 
2379
 
    @property
2380
 
    def user_transport(self):
2381
 
        return self._user_transport
2382
 
 
2383
 
    def _get_config(self):
2384
 
        return _mod_config.TransportConfig(self._transport, 'branch.conf')
2385
 
 
2386
 
    def _get_config_store(self):
2387
 
        if self.conf_store is None:
2388
 
            self.conf_store =  _mod_config.BranchStore(self)
2389
 
        return self.conf_store
2390
 
 
2391
 
    def _uncommitted_branch(self):
2392
 
        """Return the branch that may contain uncommitted changes."""
2393
 
        master = self.get_master_branch()
2394
 
        if master is not None:
2395
 
            return master
2396
 
        else:
2397
 
            return self
2398
 
 
2399
 
    def store_uncommitted(self, creator):
2400
 
        """Store uncommitted changes from a ShelfCreator.
2401
 
 
2402
 
        :param creator: The ShelfCreator containing uncommitted changes, or
2403
 
            None to delete any stored changes.
2404
 
        :raises: ChangesAlreadyStored if the branch already has changes.
2405
 
        """
2406
 
        branch = self._uncommitted_branch()
2407
 
        if creator is None:
2408
 
            branch._transport.delete('stored-transform')
2409
 
            return
2410
 
        if branch._transport.has('stored-transform'):
2411
 
            raise errors.ChangesAlreadyStored
2412
 
        transform = BytesIO()
2413
 
        creator.write_shelf(transform)
2414
 
        transform.seek(0)
2415
 
        branch._transport.put_file('stored-transform', transform)
2416
 
 
2417
 
    def get_unshelver(self, tree):
2418
 
        """Return a shelf.Unshelver for this branch and tree.
2419
 
 
2420
 
        :param tree: The tree to use to construct the Unshelver.
2421
 
        :return: an Unshelver or None if no changes are stored.
2422
 
        """
2423
 
        branch = self._uncommitted_branch()
2424
 
        try:
2425
 
            transform = branch._transport.get('stored-transform')
2426
 
        except errors.NoSuchFile:
2427
 
            return None
2428
 
        return shelf.Unshelver.from_tree_and_shelf(tree, transform)
2429
 
 
2430
 
    def is_locked(self):
2431
 
        return self.control_files.is_locked()
2432
 
 
2433
 
    def lock_write(self, token=None):
2434
 
        """Lock the branch for write operations.
2435
 
 
2436
 
        :param token: A token to permit reacquiring a previously held and
2437
 
            preserved lock.
2438
 
        :return: A BranchWriteLockResult.
2439
 
        """
2440
 
        if not self.is_locked():
2441
 
            self._note_lock('w')
2442
 
            self.repository._warn_if_deprecated(self)
2443
 
            self.repository.lock_write()
2444
 
            took_lock = True
2445
 
        else:
2446
 
            took_lock = False
2447
 
        try:
2448
 
            return BranchWriteLockResult(self.unlock,
2449
 
                self.control_files.lock_write(token=token))
2450
 
        except:
2451
 
            if took_lock:
2452
 
                self.repository.unlock()
2453
 
            raise
2454
 
 
2455
 
    def lock_read(self):
2456
 
        """Lock the branch for read operations.
2457
 
 
2458
 
        :return: A breezy.lock.LogicalLockResult.
2459
 
        """
2460
 
        if not self.is_locked():
2461
 
            self._note_lock('r')
2462
 
            self.repository._warn_if_deprecated(self)
2463
 
            self.repository.lock_read()
2464
 
            took_lock = True
2465
 
        else:
2466
 
            took_lock = False
2467
 
        try:
2468
 
            self.control_files.lock_read()
2469
 
            return LogicalLockResult(self.unlock)
2470
 
        except:
2471
 
            if took_lock:
2472
 
                self.repository.unlock()
2473
 
            raise
2474
 
 
2475
 
    @only_raises(errors.LockNotHeld, errors.LockBroken)
2476
 
    def unlock(self):
2477
 
        if self.control_files._lock_count == 1 and self.conf_store is not None:
2478
 
            self.conf_store.save_changes()
2479
 
        try:
2480
 
            self.control_files.unlock()
2481
 
        finally:
2482
 
            if not self.control_files.is_locked():
2483
 
                self.repository.unlock()
2484
 
                # we just released the lock
2485
 
                self._clear_cached_state()
2486
 
 
2487
 
    def peek_lock_mode(self):
2488
 
        if self.control_files._lock_count == 0:
2489
 
            return None
2490
 
        else:
2491
 
            return self.control_files._lock_mode
2492
 
 
2493
 
    def get_physical_lock_status(self):
2494
 
        return self.control_files.get_physical_lock_status()
2495
 
 
2496
 
    @needs_read_lock
2497
 
    def print_file(self, file, revision_id):
2498
 
        """See Branch.print_file."""
2499
 
        return self.repository.print_file(file, revision_id)
2500
 
 
2501
 
    @needs_write_lock
2502
 
    def set_last_revision_info(self, revno, revision_id):
2503
 
        if not revision_id or not isinstance(revision_id, basestring):
2504
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
2505
 
        revision_id = _mod_revision.ensure_null(revision_id)
2506
 
        old_revno, old_revid = self.last_revision_info()
2507
 
        if self.get_append_revisions_only():
2508
 
            self._check_history_violation(revision_id)
2509
 
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
2510
 
        self._write_last_revision_info(revno, revision_id)
2511
 
        self._clear_cached_state()
2512
 
        self._last_revision_info_cache = revno, revision_id
2513
 
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
2514
 
 
2515
 
    def basis_tree(self):
2516
 
        """See Branch.basis_tree."""
2517
 
        return self.repository.revision_tree(self.last_revision())
2518
 
 
2519
 
    def _get_parent_location(self):
2520
 
        _locs = ['parent', 'pull', 'x-pull']
2521
 
        for l in _locs:
2522
 
            try:
2523
 
                return self._transport.get_bytes(l).strip('\n')
2524
 
            except errors.NoSuchFile:
2525
 
                pass
2526
 
        return None
2527
 
 
2528
 
    def get_stacked_on_url(self):
2529
 
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2530
 
 
2531
 
    def set_push_location(self, location):
2532
 
        """See Branch.set_push_location."""
2533
 
        self.get_config().set_user_option(
2534
 
            'push_location', location,
2535
 
            store=_mod_config.STORE_LOCATION_NORECURSE)
2536
 
 
2537
 
    def _set_parent_location(self, url):
2538
 
        if url is None:
2539
 
            self._transport.delete('parent')
2540
 
        else:
2541
 
            self._transport.put_bytes('parent', url + '\n',
2542
 
                mode=self.bzrdir._get_file_mode())
2543
 
 
2544
 
    @needs_write_lock
2545
 
    def unbind(self):
2546
 
        """If bound, unbind"""
2547
 
        return self.set_bound_location(None)
2548
 
 
2549
 
    @needs_write_lock
2550
 
    def bind(self, other):
2551
 
        """Bind this branch to the branch other.
2552
 
 
2553
 
        This does not push or pull data between the branches, though it does
2554
 
        check for divergence to raise an error when the branches are not
2555
 
        either the same, or one a prefix of the other. That behaviour may not
2556
 
        be useful, so that check may be removed in future.
2557
 
 
2558
 
        :param other: The branch to bind to
2559
 
        :type other: Branch
2560
 
        """
2561
 
        # TODO: jam 20051230 Consider checking if the target is bound
2562
 
        #       It is debatable whether you should be able to bind to
2563
 
        #       a branch which is itself bound.
2564
 
        #       Committing is obviously forbidden,
2565
 
        #       but binding itself may not be.
2566
 
        #       Since we *have* to check at commit time, we don't
2567
 
        #       *need* to check here
2568
 
 
2569
 
        # we want to raise diverged if:
2570
 
        # last_rev is not in the other_last_rev history, AND
2571
 
        # other_last_rev is not in our history, and do it without pulling
2572
 
        # history around
2573
 
        self.set_bound_location(other.base)
2574
 
 
2575
 
    def get_bound_location(self):
2576
 
        try:
2577
 
            return self._transport.get_bytes('bound')[:-1]
2578
 
        except errors.NoSuchFile:
2579
 
            return None
2580
 
 
2581
 
    @needs_read_lock
2582
 
    def get_master_branch(self, possible_transports=None):
2583
 
        """Return the branch we are bound to.
2584
 
 
2585
 
        :return: Either a Branch, or None
2586
 
        """
2587
 
        if self._master_branch_cache is None:
2588
 
            self._master_branch_cache = self._get_master_branch(
2589
 
                possible_transports)
2590
 
        return self._master_branch_cache
2591
 
 
2592
 
    def _get_master_branch(self, possible_transports):
2593
 
        bound_loc = self.get_bound_location()
2594
 
        if not bound_loc:
2595
 
            return None
2596
 
        try:
2597
 
            return Branch.open(bound_loc,
2598
 
                               possible_transports=possible_transports)
2599
 
        except (errors.NotBranchError, errors.ConnectionError) as e:
2600
 
            raise errors.BoundBranchConnectionFailure(
2601
 
                    self, bound_loc, e)
2602
 
 
2603
 
    @needs_write_lock
2604
 
    def set_bound_location(self, location):
2605
 
        """Set the target where this branch is bound to.
2606
 
 
2607
 
        :param location: URL to the target branch
2608
 
        """
2609
 
        self._master_branch_cache = None
2610
 
        if location:
2611
 
            self._transport.put_bytes('bound', location+'\n',
2612
 
                mode=self.bzrdir._get_file_mode())
2613
 
        else:
2614
 
            try:
2615
 
                self._transport.delete('bound')
2616
 
            except errors.NoSuchFile:
2617
 
                return False
2618
 
            return True
2619
 
 
2620
 
    @needs_write_lock
2621
 
    def update(self, possible_transports=None):
2622
 
        """Synchronise this branch with the master branch if any.
2623
 
 
2624
 
        :return: None or the last_revision that was pivoted out during the
2625
 
                 update.
2626
 
        """
2627
 
        master = self.get_master_branch(possible_transports)
2628
 
        if master is not None:
2629
 
            old_tip = _mod_revision.ensure_null(self.last_revision())
2630
 
            self.pull(master, overwrite=True)
2631
 
            if self.repository.get_graph().is_ancestor(old_tip,
2632
 
                _mod_revision.ensure_null(self.last_revision())):
2633
 
                return None
2634
 
            return old_tip
2635
 
        return None
2636
 
 
2637
 
    def _read_last_revision_info(self):
2638
 
        revision_string = self._transport.get_bytes('last-revision')
2639
 
        revno, revision_id = revision_string.rstrip('\n').split(' ', 1)
2640
 
        revision_id = cache_utf8.get_cached_utf8(revision_id)
2641
 
        revno = int(revno)
2642
 
        return revno, revision_id
2643
 
 
2644
 
    def _write_last_revision_info(self, revno, revision_id):
2645
 
        """Simply write out the revision id, with no checks.
2646
 
 
2647
 
        Use set_last_revision_info to perform this safely.
2648
 
 
2649
 
        Does not update the revision_history cache.
2650
 
        """
2651
 
        revision_id = _mod_revision.ensure_null(revision_id)
2652
 
        out_string = '%d %s\n' % (revno, revision_id)
2653
 
        self._transport.put_bytes('last-revision', out_string,
2654
 
            mode=self.bzrdir._get_file_mode())
2655
 
 
2656
 
    @needs_write_lock
2657
 
    def update_feature_flags(self, updated_flags):
2658
 
        """Update the feature flags for this branch.
2659
 
 
2660
 
        :param updated_flags: Dictionary mapping feature names to necessities
2661
 
            A necessity can be None to indicate the feature should be removed
2662
 
        """
2663
 
        self._format._update_feature_flags(updated_flags)
2664
 
        self.control_transport.put_bytes('format', self._format.as_string())
2665
 
 
2666
 
 
2667
 
class BzrBranch8(BzrBranch):
2668
 
    """A branch that stores tree-reference locations."""
2669
 
 
2670
 
    def _open_hook(self, possible_transports=None):
2671
 
        if self._ignore_fallbacks:
2672
 
            return
2673
 
        if possible_transports is None:
2674
 
            possible_transports = [self.bzrdir.root_transport]
2675
 
        try:
2676
 
            url = self.get_stacked_on_url()
2677
 
        except (errors.UnstackableRepositoryFormat, errors.NotStacked,
2678
 
            errors.UnstackableBranchFormat):
2679
 
            pass
2680
 
        else:
2681
 
            for hook in Branch.hooks['transform_fallback_location']:
2682
 
                url = hook(self, url)
2683
 
                if url is None:
2684
 
                    hook_name = Branch.hooks.get_hook_name(hook)
2685
 
                    raise AssertionError(
2686
 
                        "'transform_fallback_location' hook %s returned "
2687
 
                        "None, not a URL." % hook_name)
2688
 
            self._activate_fallback_location(url,
2689
 
                possible_transports=possible_transports)
2690
 
 
2691
 
    def __init__(self, *args, **kwargs):
2692
 
        self._ignore_fallbacks = kwargs.get('ignore_fallbacks', False)
2693
 
        super(BzrBranch8, self).__init__(*args, **kwargs)
2694
 
        self._last_revision_info_cache = None
2695
 
        self._reference_info = None
2696
 
 
2697
 
    def _clear_cached_state(self):
2698
 
        super(BzrBranch8, self)._clear_cached_state()
2699
 
        self._last_revision_info_cache = None
2700
 
        self._reference_info = None
2701
 
 
2702
 
    def _check_history_violation(self, revision_id):
2703
 
        current_revid = self.last_revision()
2704
 
        last_revision = _mod_revision.ensure_null(current_revid)
2705
 
        if _mod_revision.is_null(last_revision):
2706
 
            return
2707
 
        graph = self.repository.get_graph()
2708
 
        for lh_ancestor in graph.iter_lefthand_ancestry(revision_id):
2709
 
            if lh_ancestor == current_revid:
2710
 
                return
2711
 
        raise errors.AppendRevisionsOnlyViolation(self.user_url)
2712
 
 
2713
 
    def _gen_revision_history(self):
2714
 
        """Generate the revision history from last revision
2715
 
        """
2716
 
        last_revno, last_revision = self.last_revision_info()
2717
 
        self._extend_partial_history(stop_index=last_revno-1)
2718
 
        return list(reversed(self._partial_revision_history_cache))
2719
 
 
2720
 
    @needs_write_lock
2721
 
    def _set_parent_location(self, url):
2722
 
        """Set the parent branch"""
2723
 
        self._set_config_location('parent_location', url, make_relative=True)
2724
 
 
2725
 
    @needs_read_lock
2726
 
    def _get_parent_location(self):
2727
 
        """Set the parent branch"""
2728
 
        return self._get_config_location('parent_location')
2729
 
 
2730
 
    @needs_write_lock
2731
 
    def _set_all_reference_info(self, info_dict):
2732
 
        """Replace all reference info stored in a branch.
2733
 
 
2734
 
        :param info_dict: A dict of {file_id: (tree_path, branch_location)}
2735
 
        """
2736
 
        s = BytesIO()
2737
 
        writer = rio.RioWriter(s)
2738
 
        for key, (tree_path, branch_location) in viewitems(info_dict):
2739
 
            stanza = rio.Stanza(file_id=key, tree_path=tree_path,
2740
 
                                branch_location=branch_location)
2741
 
            writer.write_stanza(stanza)
2742
 
        self._transport.put_bytes('references', s.getvalue())
2743
 
        self._reference_info = info_dict
2744
 
 
2745
 
    @needs_read_lock
2746
 
    def _get_all_reference_info(self):
2747
 
        """Return all the reference info stored in a branch.
2748
 
 
2749
 
        :return: A dict of {file_id: (tree_path, branch_location)}
2750
 
        """
2751
 
        if self._reference_info is not None:
2752
 
            return self._reference_info
2753
 
        rio_file = self._transport.get('references')
2754
 
        try:
2755
 
            stanzas = rio.read_stanzas(rio_file)
2756
 
            info_dict = dict((s['file_id'], (s['tree_path'],
2757
 
                             s['branch_location'])) for s in stanzas)
2758
 
        finally:
2759
 
            rio_file.close()
2760
 
        self._reference_info = info_dict
2761
 
        return info_dict
2762
 
 
2763
 
    def set_reference_info(self, file_id, tree_path, branch_location):
2764
 
        """Set the branch location to use for a tree reference.
2765
 
 
2766
 
        :param file_id: The file-id of the tree reference.
2767
 
        :param tree_path: The path of the tree reference in the tree.
2768
 
        :param branch_location: The location of the branch to retrieve tree
2769
 
            references from.
2770
 
        """
2771
 
        info_dict = self._get_all_reference_info()
2772
 
        info_dict[file_id] = (tree_path, branch_location)
2773
 
        if None in (tree_path, branch_location):
2774
 
            if tree_path is not None:
2775
 
                raise ValueError('tree_path must be None when branch_location'
2776
 
                                 ' is None.')
2777
 
            if branch_location is not None:
2778
 
                raise ValueError('branch_location must be None when tree_path'
2779
 
                                 ' is None.')
2780
 
            del info_dict[file_id]
2781
 
        self._set_all_reference_info(info_dict)
2782
 
 
2783
 
    def get_reference_info(self, file_id):
2784
 
        """Get the tree_path and branch_location for a tree reference.
2785
 
 
2786
 
        :return: a tuple of (tree_path, branch_location)
2787
 
        """
2788
 
        return self._get_all_reference_info().get(file_id, (None, None))
2789
 
 
2790
 
    def reference_parent(self, file_id, path, possible_transports=None):
2791
 
        """Return the parent branch for a tree-reference file_id.
2792
 
 
2793
 
        :param file_id: The file_id of the tree reference
2794
 
        :param path: The path of the file_id in the tree
2795
 
        :return: A branch associated with the file_id
2796
 
        """
2797
 
        branch_location = self.get_reference_info(file_id)[1]
2798
 
        if branch_location is None:
2799
 
            return Branch.reference_parent(self, file_id, path,
2800
 
                                           possible_transports)
2801
 
        branch_location = urlutils.join(self.user_url, branch_location)
2802
 
        return Branch.open(branch_location,
2803
 
                           possible_transports=possible_transports)
2804
 
 
2805
 
    def set_push_location(self, location):
2806
 
        """See Branch.set_push_location."""
2807
 
        self._set_config_location('push_location', location)
2808
 
 
2809
 
    def set_bound_location(self, location):
2810
 
        """See Branch.set_push_location."""
2811
 
        self._master_branch_cache = None
2812
 
        result = None
2813
 
        conf = self.get_config_stack()
2814
 
        if location is None:
2815
 
            if not conf.get('bound'):
2816
 
                return False
2817
 
            else:
2818
 
                conf.set('bound', 'False')
2819
 
                return True
2820
 
        else:
2821
 
            self._set_config_location('bound_location', location,
2822
 
                                      config=conf)
2823
 
            conf.set('bound', 'True')
2824
 
        return True
2825
 
 
2826
 
    def _get_bound_location(self, bound):
2827
 
        """Return the bound location in the config file.
2828
 
 
2829
 
        Return None if the bound parameter does not match"""
2830
 
        conf = self.get_config_stack()
2831
 
        if conf.get('bound') != bound:
2832
 
            return None
2833
 
        return self._get_config_location('bound_location', config=conf)
2834
 
 
2835
 
    def get_bound_location(self):
2836
 
        """See Branch.get_bound_location."""
2837
 
        return self._get_bound_location(True)
2838
 
 
2839
 
    def get_old_bound_location(self):
2840
 
        """See Branch.get_old_bound_location"""
2841
 
        return self._get_bound_location(False)
2842
 
 
2843
 
    def get_stacked_on_url(self):
2844
 
        # you can always ask for the URL; but you might not be able to use it
2845
 
        # if the repo can't support stacking.
2846
 
        ## self._check_stackable_repo()
2847
 
        # stacked_on_location is only ever defined in branch.conf, so don't
2848
 
        # waste effort reading the whole stack of config files.
2849
 
        conf = _mod_config.BranchOnlyStack(self)
2850
 
        stacked_url = self._get_config_location('stacked_on_location',
2851
 
                                                config=conf)
2852
 
        if stacked_url is None:
2853
 
            raise errors.NotStacked(self)
2854
 
        return stacked_url.encode('utf-8')
2855
 
 
2856
 
    @needs_read_lock
2857
 
    def get_rev_id(self, revno, history=None):
2858
 
        """Find the revision id of the specified revno."""
2859
 
        if revno == 0:
2860
 
            return _mod_revision.NULL_REVISION
2861
 
 
2862
 
        last_revno, last_revision_id = self.last_revision_info()
2863
 
        if revno <= 0 or revno > last_revno:
2864
 
            raise errors.NoSuchRevision(self, revno)
2865
 
 
2866
 
        if history is not None:
2867
 
            return history[revno - 1]
2868
 
 
2869
 
        index = last_revno - revno
2870
 
        if len(self._partial_revision_history_cache) <= index:
2871
 
            self._extend_partial_history(stop_index=index)
2872
 
        if len(self._partial_revision_history_cache) > index:
2873
 
            return self._partial_revision_history_cache[index]
2874
 
        else:
2875
 
            raise errors.NoSuchRevision(self, revno)
2876
 
 
2877
 
    @needs_read_lock
2878
 
    def revision_id_to_revno(self, revision_id):
2879
 
        """Given a revision id, return its revno"""
2880
 
        if _mod_revision.is_null(revision_id):
2881
 
            return 0
2882
 
        try:
2883
 
            index = self._partial_revision_history_cache.index(revision_id)
2884
 
        except ValueError:
2885
 
            try:
2886
 
                self._extend_partial_history(stop_revision=revision_id)
2887
 
            except errors.RevisionNotPresent as e:
2888
 
                raise errors.GhostRevisionsHaveNoRevno(revision_id, e.revision_id)
2889
 
            index = len(self._partial_revision_history_cache) - 1
2890
 
            if index < 0:
2891
 
                raise errors.NoSuchRevision(self, revision_id)
2892
 
            if self._partial_revision_history_cache[index] != revision_id:
2893
 
                raise errors.NoSuchRevision(self, revision_id)
2894
 
        return self.revno() - index
2895
 
 
2896
 
 
2897
 
class BzrBranch7(BzrBranch8):
2898
 
    """A branch with support for a fallback repository."""
2899
 
 
2900
 
    def set_reference_info(self, file_id, tree_path, branch_location):
2901
 
        Branch.set_reference_info(self, file_id, tree_path, branch_location)
2902
 
 
2903
 
    def get_reference_info(self, file_id):
2904
 
        Branch.get_reference_info(self, file_id)
2905
 
 
2906
 
    def reference_parent(self, file_id, path, possible_transports=None):
2907
 
        return Branch.reference_parent(self, file_id, path,
2908
 
                                       possible_transports)
2909
 
 
2910
 
 
2911
 
class BzrBranch6(BzrBranch7):
2912
 
    """See BzrBranchFormat6 for the capabilities of this branch.
2913
 
 
2914
 
    This subclass of BzrBranch7 disables the new features BzrBranch7 added,
2915
 
    i.e. stacking.
2916
 
    """
2917
 
 
2918
 
    def get_stacked_on_url(self):
2919
 
        raise errors.UnstackableBranchFormat(self._format, self.user_url)
2920
 
 
2921
 
 
2922
1966
######################################################################
2923
1967
# results of operations
2924
1968
 
3026
2070
            note(gettext('found error:%s'), error)
3027
2071
 
3028
2072
 
3029
 
class Converter5to6(object):
3030
 
    """Perform an in-place upgrade of format 5 to format 6"""
3031
 
 
3032
 
    def convert(self, branch):
3033
 
        # Data for 5 and 6 can peacefully coexist.
3034
 
        format = BzrBranchFormat6()
3035
 
        new_branch = format.open(branch.bzrdir, _found=True)
3036
 
 
3037
 
        # Copy source data into target
3038
 
        new_branch._write_last_revision_info(*branch.last_revision_info())
3039
 
        new_branch.lock_write()
3040
 
        try:
3041
 
            new_branch.set_parent(branch.get_parent())
3042
 
            new_branch.set_bound_location(branch.get_bound_location())
3043
 
            new_branch.set_push_location(branch.get_push_location())
3044
 
        finally:
3045
 
            new_branch.unlock()
3046
 
 
3047
 
        # New branch has no tags by default
3048
 
        new_branch.tags._set_tag_dict({})
3049
 
 
3050
 
        # Copying done; now update target format
3051
 
        new_branch._transport.put_bytes('format',
3052
 
            format.as_string(),
3053
 
            mode=new_branch.bzrdir._get_file_mode())
3054
 
 
3055
 
        # Clean up old files
3056
 
        new_branch._transport.delete('revision-history')
3057
 
        branch.lock_write()
3058
 
        try:
3059
 
            try:
3060
 
                branch.set_parent(None)
3061
 
            except errors.NoSuchFile:
3062
 
                pass
3063
 
            branch.set_bound_location(None)
3064
 
        finally:
3065
 
            branch.unlock()
3066
 
 
3067
 
 
3068
 
class Converter6to7(object):
3069
 
    """Perform an in-place upgrade of format 6 to format 7"""
3070
 
 
3071
 
    def convert(self, branch):
3072
 
        format = BzrBranchFormat7()
3073
 
        branch._set_config_location('stacked_on_location', '')
3074
 
        # update target format
3075
 
        branch._transport.put_bytes('format', format.as_string())
3076
 
 
3077
 
 
3078
 
class Converter7to8(object):
3079
 
    """Perform an in-place upgrade of format 7 to format 8"""
3080
 
 
3081
 
    def convert(self, branch):
3082
 
        format = BzrBranchFormat8()
3083
 
        branch._transport.put_bytes('references', '')
3084
 
        # update target format
3085
 
        branch._transport.put_bytes('format', format.as_string())
3086
 
 
3087
 
 
3088
2073
class InterBranch(InterObject):
3089
2074
    """This class represents operations taking place between two branches.
3090
2075