~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/bzrdir.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-04-27 06:47:50 UTC
  • mfrom: (4294.2.12 push.roundtrips)
  • Revision ID: pqm@pqm.ubuntu.com-20090427064750-e9obd6h83omt86ps
(robertc) Reduce roundtrips needed to push a new branch by coalescing
        many steps of the initialisation process. (Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
37
37
 
38
38
import bzrlib
39
39
from bzrlib import (
 
40
    branch,
40
41
    config,
41
42
    errors,
42
43
    graph,
44
45
    lockdir,
45
46
    osutils,
46
47
    remote,
 
48
    repository,
47
49
    revision as _mod_revision,
48
50
    ui,
49
51
    urlutils,
60
62
from bzrlib.push import (
61
63
    PushResult,
62
64
    )
 
65
from bzrlib.repofmt import pack_repo
63
66
from bzrlib.smart.client import _SmartClient
64
67
from bzrlib.store.versioned import WeaveStore
65
68
from bzrlib.transactions import WriteTransaction
178
181
                                       preserve_stacking=preserve_stacking)
179
182
 
180
183
    def clone_on_transport(self, transport, revision_id=None,
181
 
                           force_new_repo=False, preserve_stacking=False,
182
 
                           stacked_on=None):
 
184
        force_new_repo=False, preserve_stacking=False, stacked_on=None,
 
185
        create_prefix=False, use_existing_dir=True):
183
186
        """Clone this bzrdir and its contents to transport verbatim.
184
187
 
185
188
        :param transport: The transport for the location to produce the clone
191
194
                               even if one is available.
192
195
        :param preserve_stacking: When cloning a stacked branch, stack the
193
196
            new branch on top of the other branch's stacked-on branch.
 
197
        :param create_prefix: Create any missing directories leading up to
 
198
            to_transport.
 
199
        :param use_existing_dir: Use an existing directory if one exists.
194
200
        """
195
 
        transport.ensure_base()
 
201
        # Overview: put together a broad description of what we want to end up
 
202
        # with; then make as few api calls as possible to do it.
 
203
        
 
204
        # We may want to create a repo/branch/tree, if we do so what format
 
205
        # would we want for each:
196
206
        require_stacking = (stacked_on is not None)
197
207
        format = self.cloning_metadir(require_stacking)
198
 
        # Bug: We create a metadir without knowing if it can support stacking,
199
 
        # we should look up the policy needs first.
200
 
        result = format.initialize_on_transport(transport)
201
 
        repository_policy = None
 
208
        
 
209
        # Figure out what objects we want:
202
210
        try:
203
211
            local_repo = self.find_repository()
204
212
        except errors.NoRepositoryPresent:
218
226
                        errors.UnstackableRepositoryFormat,
219
227
                        errors.NotStacked):
220
228
                    pass
221
 
 
 
229
        # Bug: We create a metadir without knowing if it can support stacking,
 
230
        # we should look up the policy needs first, or just use it as a hint,
 
231
        # or something.
222
232
        if local_repo:
223
 
            # may need to copy content in
224
 
            repository_policy = result.determine_repository_policy(
225
 
                force_new_repo, stacked_on, self.root_transport.base,
226
 
                require_stacking=require_stacking)
227
233
            make_working_trees = local_repo.make_working_trees()
228
 
            result_repo, is_new_repo = repository_policy.acquire_repository(
229
 
                make_working_trees, local_repo.is_shared())
230
 
            if not require_stacking and repository_policy._require_stacking:
231
 
                require_stacking = True
232
 
                result._format.require_stacking()
233
 
            if is_new_repo and not require_stacking and revision_id is not None:
 
234
            want_shared = local_repo.is_shared()
 
235
            repo_format_name = format.repository_format.network_name()
 
236
        else:
 
237
            make_working_trees = False
 
238
            want_shared = False
 
239
            repo_format_name = None
 
240
 
 
241
        result_repo, result, require_stacking, repository_policy = \
 
242
            format.initialize_on_transport_ex(transport,
 
243
            use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
244
            force_new_repo=force_new_repo, stacked_on=stacked_on,
 
245
            stack_on_pwd=self.root_transport.base,
 
246
            repo_format_name=repo_format_name,
 
247
            make_working_trees=make_working_trees, shared_repo=want_shared)
 
248
        if repo_format_name:
 
249
            # If the result repository is in the same place as the resulting
 
250
            # bzr dir, it will have no content, further if the result is not stacked
 
251
            # then we know all content should be copied, and finally if we are
 
252
            # copying up to a specific revision_id then we can use the
 
253
            # pending-ancestry-result which does not require traversing all of
 
254
            # history to describe it.
 
255
            if (result_repo.bzrdir.root_transport.base ==
 
256
                result.root_transport.base and not require_stacking and
 
257
                revision_id is not None):
234
258
                fetch_spec = graph.PendingAncestryResult(
235
259
                    [revision_id], local_repo)
236
260
                result_repo.fetch(local_repo, fetch_spec=fetch_spec)
1067
1091
        """
1068
1092
        format, repository = self._cloning_metadir()
1069
1093
        if format._workingtree_format is None:
 
1094
            # No tree in self.
1070
1095
            if repository is None:
 
1096
                # No repository either
1071
1097
                return format
 
1098
            # We have a repository, so set a working tree? (Why? This seems to
 
1099
            # contradict the stated return value in the docstring).
1072
1100
            tree_format = repository._format._matchingbzrdir.workingtree_format
1073
1101
            format.workingtree_format = tree_format.__class__()
1074
1102
        if require_stacking:
1700
1728
    def _get_config(self):
1701
1729
        return config.TransportConfig(self.transport, 'control.conf')
1702
1730
 
 
1731
 
1703
1732
class BzrDirFormat(object):
1704
1733
    """An encapsulation of the initialization and open routines for a format.
1705
1734
 
1798
1827
    def initialize(self, url, possible_transports=None):
1799
1828
        """Create a bzr control dir at this url and return an opened copy.
1800
1829
 
 
1830
        While not deprecated, this method is very specific and its use will
 
1831
        lead to many round trips to setup a working environment. See
 
1832
        initialize_on_transport_ex for a [nearly] all-in-one method.
 
1833
 
1801
1834
        Subclasses should typically override initialize_on_transport
1802
1835
        instead of this method.
1803
1836
        """
1822
1855
            self._supply_sub_formats_to(remote_format)
1823
1856
            return remote_format.initialize_on_transport(transport)
1824
1857
 
 
1858
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
 
1859
        create_prefix=False, force_new_repo=False, stacked_on=None,
 
1860
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
 
1861
        shared_repo=False, vfs_only=False):
 
1862
        """Create this format on transport.
 
1863
 
 
1864
        The directory to initialize will be created.
 
1865
 
 
1866
        :param force_new_repo: Do not use a shared repository for the target,
 
1867
                               even if one is available.
 
1868
        :param create_prefix: Create any missing directories leading up to
 
1869
            to_transport.
 
1870
        :param use_existing_dir: Use an existing directory if one exists.
 
1871
        :param stacked_on: A url to stack any created branch on, None to follow
 
1872
            any target stacking policy.
 
1873
        :param stack_on_pwd: If stack_on is relative, the location it is
 
1874
            relative to.
 
1875
        :param repo_format_name: If non-None, a repository will be
 
1876
            made-or-found. Should none be found, or if force_new_repo is True
 
1877
            the repo_format_name is used to select the format of repository to
 
1878
            create.
 
1879
        :param make_working_trees: Control the setting of make_working_trees
 
1880
            for a new shared repository when one is made. None to use whatever
 
1881
            default the format has.
 
1882
        :param shared_repo: Control whether made repositories are shared or
 
1883
            not.
 
1884
        :param vfs_only: If True do not attempt to use a smart server
 
1885
        :return: repo, bzrdir, require_stacking, repository_policy. repo is
 
1886
            None if none was created or found, bzrdir is always valid.
 
1887
            require_stacking is the result of examining the stacked_on
 
1888
            parameter and any stacking policy found for the target.
 
1889
        """
 
1890
        if not vfs_only:
 
1891
            # Try to hand off to a smart server 
 
1892
            try:
 
1893
                client_medium = transport.get_smart_medium()
 
1894
            except errors.NoSmartMedium:
 
1895
                pass
 
1896
            else:
 
1897
                # TODO: lookup the local format from a server hint.
 
1898
                remote_dir_format = RemoteBzrDirFormat()
 
1899
                remote_dir_format._network_name = self.network_name()
 
1900
                self._supply_sub_formats_to(remote_dir_format)
 
1901
                return remote_dir_format.initialize_on_transport_ex(transport,
 
1902
                    use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
1903
                    force_new_repo=force_new_repo, stacked_on=stacked_on,
 
1904
                    stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
1905
                    make_working_trees=make_working_trees, shared_repo=shared_repo)
 
1906
        # XXX: Refactor the create_prefix/no_create_prefix code into a
 
1907
        #      common helper function
 
1908
        # The destination may not exist - if so make it according to policy.
 
1909
        def make_directory(transport):
 
1910
            transport.mkdir('.')
 
1911
            return transport
 
1912
        def redirected(transport, e, redirection_notice):
 
1913
            note(redirection_notice)
 
1914
            return transport._redirected_to(e.source, e.target)
 
1915
        try:
 
1916
            transport = do_catching_redirections(make_directory, transport,
 
1917
                redirected)
 
1918
        except errors.FileExists:
 
1919
            if not use_existing_dir:
 
1920
                raise
 
1921
        except errors.NoSuchFile:
 
1922
            if not create_prefix:
 
1923
                raise
 
1924
            transport.create_prefix()
 
1925
 
 
1926
        require_stacking = (stacked_on is not None)
 
1927
        # Now the target directory exists, but doesn't have a .bzr
 
1928
        # directory. So we need to create it, along with any work to create
 
1929
        # all of the dependent branches, etc.
 
1930
 
 
1931
        result = self.initialize_on_transport(transport)
 
1932
        if repo_format_name:
 
1933
            try:
 
1934
                # use a custom format
 
1935
                result._format.repository_format = \
 
1936
                    repository.network_format_registry.get(repo_format_name)
 
1937
            except AttributeError:
 
1938
                # The format didn't permit it to be set.
 
1939
                pass
 
1940
            # A repository is desired, either in-place or shared.
 
1941
            repository_policy = result.determine_repository_policy(
 
1942
                force_new_repo, stacked_on, stack_on_pwd,
 
1943
                require_stacking=require_stacking)
 
1944
            result_repo, is_new_repo = repository_policy.acquire_repository(
 
1945
                make_working_trees, shared_repo)
 
1946
            if not require_stacking and repository_policy._require_stacking:
 
1947
                require_stacking = True
 
1948
                result._format.require_stacking()
 
1949
        else:
 
1950
            result_repo = None
 
1951
            repository_policy = None
 
1952
        return result_repo, result, require_stacking, repository_policy
 
1953
 
1825
1954
    def _initialize_on_transport_vfs(self, transport):
1826
1955
        """Initialize a new bzrdir using VFS calls.
1827
1956
 
2039
2168
    repository_format = property(__return_repository_format)
2040
2169
 
2041
2170
 
2042
 
class BzrDirFormat5(BzrDirFormat):
 
2171
class BzrDirFormatAllInOne(BzrDirFormat):
 
2172
    """Common class for formats before meta-dirs."""
 
2173
 
 
2174
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
 
2175
        create_prefix=False, force_new_repo=False, stacked_on=None,
 
2176
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
 
2177
        shared_repo=False):
 
2178
        """See BzrDirFormat.initialize_on_transport_ex."""
 
2179
        require_stacking = (stacked_on is not None)
 
2180
        # Format 5 cannot stack, but we've been asked to - actually init
 
2181
        # a Meta1Dir
 
2182
        if require_stacking:
 
2183
            format = BzrDirMetaFormat1()
 
2184
            return format.initialize_on_transport_ex(transport,
 
2185
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
2186
                force_new_repo=force_new_repo, stacked_on=stacked_on,
 
2187
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
2188
                make_working_trees=make_working_trees, shared_repo=shared_repo)
 
2189
        return BzrDirFormat.initialize_on_transport_ex(self, transport,
 
2190
            use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
2191
            force_new_repo=force_new_repo, stacked_on=stacked_on,
 
2192
            stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
2193
            make_working_trees=make_working_trees, shared_repo=shared_repo)
 
2194
 
 
2195
 
 
2196
class BzrDirFormat5(BzrDirFormatAllInOne):
2043
2197
    """Bzr control format 5.
2044
2198
 
2045
2199
    This format is a combined format for working tree, branch and repository.
2100
2254
    repository_format = property(__return_repository_format)
2101
2255
 
2102
2256
 
2103
 
class BzrDirFormat6(BzrDirFormat):
 
2257
class BzrDirFormat6(BzrDirFormatAllInOne):
2104
2258
    """Bzr control format 6.
2105
2259
 
2106
2260
    This format is a combined format for working tree, branch and repository.
2242
2396
 
2243
2397
    def _open(self, transport):
2244
2398
        """See BzrDirFormat._open."""
2245
 
        return BzrDirMeta1(transport, self)
 
2399
        # Create a new format instance because otherwise initialisation of new
 
2400
        # metadirs share the global default format object leading to alias
 
2401
        # problems.
 
2402
        format = BzrDirMetaFormat1()
 
2403
        self._supply_sub_formats_to(format)
 
2404
        return BzrDirMeta1(transport, format)
2246
2405
 
2247
2406
    def __return_repository_format(self):
2248
2407
        """Circular import protection."""
2828
2987
        return to_convert
2829
2988
 
2830
2989
 
2831
 
# This is not in remote.py because it's small, and needs to be registered.
2832
 
# Putting it in remote.py creates a circular import problem.
 
2990
# This is not in remote.py because it's relatively small, and needs to be
 
2991
# registered. Putting it in remote.py creates a circular import problem.
2833
2992
# we can make it a lazy object if the control formats is turned into something
2834
2993
# like a registry.
2835
2994
class RemoteBzrDirFormat(BzrDirMetaFormat1):
2892
3051
        self._supply_sub_formats_to(format)
2893
3052
        return remote.RemoteBzrDir(transport, format)
2894
3053
 
 
3054
    def parse_NoneTrueFalse(self, arg):
 
3055
        if not arg:
 
3056
            return None
 
3057
        if arg == 'False':
 
3058
            return False
 
3059
        if arg == 'True':
 
3060
            return True
 
3061
        raise AssertionError("invalid arg %r" % arg)
 
3062
 
 
3063
    def _serialize_NoneTrueFalse(self, arg):
 
3064
        if arg is False:
 
3065
            return 'False'
 
3066
        if arg:
 
3067
            return 'True'
 
3068
        return ''
 
3069
 
 
3070
    def _serialize_NoneString(self, arg):
 
3071
        return arg or ''
 
3072
 
 
3073
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
 
3074
        create_prefix=False, force_new_repo=False, stacked_on=None,
 
3075
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
 
3076
        shared_repo=False):
 
3077
        try:
 
3078
            # hand off the request to the smart server
 
3079
            client_medium = transport.get_smart_medium()
 
3080
        except errors.NoSmartMedium:
 
3081
            do_vfs = True
 
3082
        else:
 
3083
            # Decline to open it if the server doesn't support our required
 
3084
            # version (3) so that the VFS-based transport will do it.
 
3085
            if client_medium.should_probe():
 
3086
                try:
 
3087
                    server_version = client_medium.protocol_version()
 
3088
                    if server_version != '2':
 
3089
                        do_vfs = True
 
3090
                    else:
 
3091
                        do_vfs = False
 
3092
                except errors.SmartProtocolError:
 
3093
                    # Apparently there's no usable smart server there, even though
 
3094
                    # the medium supports the smart protocol.
 
3095
                    do_vfs = True
 
3096
            else:
 
3097
                do_vfs = False
 
3098
        if not do_vfs:
 
3099
            client = _SmartClient(client_medium)
 
3100
            path = client.remote_path_from_transport(transport)
 
3101
            if client_medium._is_remote_before((1, 15)):
 
3102
                do_vfs = True
 
3103
        if do_vfs:
 
3104
            # TODO: lookup the local format from a server hint.
 
3105
            local_dir_format = BzrDirMetaFormat1()
 
3106
            self._supply_sub_formats_to(local_dir_format)
 
3107
            return local_dir_format.initialize_on_transport_ex(transport,
 
3108
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
3109
                force_new_repo=force_new_repo, stacked_on=stacked_on,
 
3110
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
3111
                make_working_trees=make_working_trees, shared_repo=shared_repo,
 
3112
                vfs_only=True)
 
3113
        args = []
 
3114
        args.append(self._serialize_NoneTrueFalse(use_existing_dir))
 
3115
        args.append(self._serialize_NoneTrueFalse(create_prefix))
 
3116
        args.append(self._serialize_NoneTrueFalse(force_new_repo))
 
3117
        args.append(self._serialize_NoneString(stacked_on))
 
3118
        # stack_on_pwd is often/usually our transport
 
3119
        if stack_on_pwd:
 
3120
            try:
 
3121
                stack_on_pwd = transport.relpath(stack_on_pwd)
 
3122
                if not stack_on_pwd:
 
3123
                    stack_on_pwd = '.'
 
3124
            except errors.PathNotChild:
 
3125
                pass
 
3126
        args.append(self._serialize_NoneString(stack_on_pwd))
 
3127
        args.append(self._serialize_NoneString(repo_format_name))
 
3128
        args.append(self._serialize_NoneTrueFalse(make_working_trees))
 
3129
        args.append(self._serialize_NoneTrueFalse(shared_repo))
 
3130
        if self._network_name is None:
 
3131
            self._network_name = \
 
3132
            BzrDirFormat.get_default_format().network_name()
 
3133
        try:
 
3134
            response = client.call('BzrDirFormat.initialize_ex',
 
3135
                self.network_name(), path, *args)
 
3136
        except errors.UnknownSmartMethod:
 
3137
            local_dir_format = BzrDirMetaFormat1()
 
3138
            self._supply_sub_formats_to(local_dir_format)
 
3139
            return local_dir_format.initialize_on_transport_ex(transport,
 
3140
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
3141
                force_new_repo=force_new_repo, stacked_on=stacked_on,
 
3142
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
3143
                make_working_trees=make_working_trees, shared_repo=shared_repo,
 
3144
                vfs_only=True)
 
3145
        repo_path = response[0]
 
3146
        bzrdir_name = response[6]
 
3147
        require_stacking = response[7]
 
3148
        require_stacking = self.parse_NoneTrueFalse(require_stacking)
 
3149
        format = RemoteBzrDirFormat()
 
3150
        format._network_name = bzrdir_name
 
3151
        self._supply_sub_formats_to(format)
 
3152
        bzrdir = remote.RemoteBzrDir(transport, format)
 
3153
        if repo_path:
 
3154
            repo_format = remote.response_tuple_to_repo_format(response[1:])
 
3155
            if repo_path == '.':
 
3156
                repo_path = ''
 
3157
            if repo_path:
 
3158
                repo_bzrdir_format = RemoteBzrDirFormat()
 
3159
                repo_bzrdir_format._network_name = response[5]
 
3160
                repo_bzr = remote.RemoteBzrDir(transport.clone(repo_path),
 
3161
                    repo_bzrdir_format)
 
3162
            else:
 
3163
                repo_bzr = bzrdir
 
3164
            final_stack = response[8] or None
 
3165
            final_stack_pwd = response[9] or None
 
3166
            remote_repo = remote.RemoteRepository(repo_bzr, repo_format)
 
3167
            policy = UseExistingRepository(remote_repo, final_stack,
 
3168
                final_stack_pwd, require_stacking)
 
3169
            policy.acquire_repository()
 
3170
        else:
 
3171
            remote_repo = None
 
3172
            policy = None
 
3173
        return remote_repo, bzrdir, require_stacking, policy
 
3174
 
2895
3175
    def _open(self, transport):
2896
3176
        return remote.RemoteBzrDir(transport, self)
2897
3177
 
3171
3451
        stack_on = self._get_full_stack_on()
3172
3452
        if stack_on is None:
3173
3453
            return
3174
 
        stacked_dir = BzrDir.open(stack_on,
3175
 
                                  possible_transports=possible_transports)
 
3454
        try:
 
3455
            stacked_dir = BzrDir.open(stack_on,
 
3456
                                      possible_transports=possible_transports)
 
3457
        except errors.JailBreak:
 
3458
            # We keep the stacking details, but we are in the server code so
 
3459
            # actually stacking is not needed.
 
3460
            return
3176
3461
        try:
3177
3462
            stacked_repo = stacked_dir.open_branch().repository
3178
3463
        except errors.NotBranchError:
3233
3518
                # network round trips to check - but we only do this
3234
3519
                # when the source can't stack so it will fade away
3235
3520
                # as people do upgrade.
 
3521
                branch_format = None
 
3522
                repo_format = None
3236
3523
                try:
3237
3524
                    target_dir = BzrDir.open(stack_on,
3238
3525
                        possible_transports=[self._bzrdir.root_transport])
3239
3526
                except errors.NotBranchError:
3240
3527
                    # Nothing there, don't change formats
3241
3528
                    pass
 
3529
                except errors.JailBreak:
 
3530
                    # stack_on is inaccessible, JFDI.
 
3531
                    if format.repository_format.rich_root_data:
 
3532
                        repo_format = pack_repo.RepositoryFormatKnitPack6RichRoot()
 
3533
                    else:
 
3534
                        repo_format = pack_repo.RepositoryFormatKnitPack6()
 
3535
                    branch_format = branch.BzrBranchFormat7()
3242
3536
                else:
3243
3537
                    try:
3244
3538
                        target_branch = target_dir.open_branch()
3251
3545
                        if not (branch_format.supports_stacking()
3252
3546
                            and repo_format.supports_external_lookups):
3253
3547
                            # Doesn't stack itself, don't force an upgrade
3254
 
                            pass
3255
 
                        else:
3256
 
                            # Does support stacking, use its format.
3257
 
                            format.repository_format = repo_format
3258
 
                            format.set_branch_format(branch_format)
3259
 
                            note('Source format does not support stacking, '
3260
 
                                'using format: \'%s\'\n  %s\n',
3261
 
                                branch_format.get_format_description(),
3262
 
                                repo_format.get_format_description())
 
3548
                            branch_format = None
 
3549
                            repo_format = None
 
3550
                if branch_format and repo_format:
 
3551
                    # Does support stacking, use its format.
 
3552
                    format.repository_format = repo_format
 
3553
                    format.set_branch_format(branch_format)
 
3554
                    note('Source format does not support stacking, '
 
3555
                        'using format: \'%s\'\n  %s\n',
 
3556
                        branch_format.get_format_description(),
 
3557
                        repo_format.get_format_description())
3263
3558
            if not self._require_stacking:
3264
3559
                # We have picked up automatic stacking somewhere.
3265
3560
                note('Using default stacking branch %s at %s', self._stack_on,