/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 bzrlib/remote.py

  • Committer: Robert Collins
  • Date: 2010-05-06 11:08:10 UTC
  • mto: This revision was merged to the branch mainline in revision 5223.
  • Revision ID: robertc@robertcollins.net-20100506110810-h3j07fh5gmw54s25
Cleaner matcher matching revised unlocking protocol.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2011 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
21
21
    branch,
22
22
    bzrdir,
23
23
    config,
24
 
    controldir,
25
24
    debug,
26
25
    errors,
27
26
    graph,
33
32
    revision as _mod_revision,
34
33
    static_tuple,
35
34
    symbol_versioning,
36
 
    urlutils,
37
35
)
38
 
from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult
 
36
from bzrlib.branch import BranchReferenceFormat
39
37
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
40
38
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
41
39
from bzrlib.errors import (
45
43
from bzrlib.lockable_files import LockableFiles
46
44
from bzrlib.smart import client, vfs, repository as smart_repo
47
45
from bzrlib.revision import ensure_null, NULL_REVISION
48
 
from bzrlib.repository import RepositoryWriteLockResult
49
46
from bzrlib.trace import mutter, note, warning
50
47
 
51
48
 
215
212
        if len(branch_info) != 2:
216
213
            raise errors.UnexpectedSmartServerResponse(response)
217
214
        branch_ref, branch_name = branch_info
218
 
        format = controldir.network_format_registry.get(control_name)
 
215
        format = bzrdir.network_format_registry.get(control_name)
219
216
        if repo_name:
220
217
            format.repository_format = repository.network_format_registry.get(
221
218
                repo_name)
247
244
        self._ensure_real()
248
245
        self._real_bzrdir.destroy_repository()
249
246
 
250
 
    def create_branch(self, name=None, repository=None):
 
247
    def create_branch(self, name=None):
251
248
        # as per meta1 formats - just delegate to the format object which may
252
249
        # be parameterised.
253
250
        real_branch = self._format.get_branch_format().initialize(self,
254
 
            name=name, repository=repository)
 
251
            name=name)
255
252
        if not isinstance(real_branch, RemoteBranch):
256
 
            if not isinstance(repository, RemoteRepository):
257
 
                raise AssertionError(
258
 
                    'need a RemoteRepository to use with RemoteBranch, got %r'
259
 
                    % (repository,))
260
 
            result = RemoteBranch(self, repository, real_branch, name=name)
 
253
            result = RemoteBranch(self, self.find_repository(), real_branch,
 
254
                                  name=name)
261
255
        else:
262
256
            result = real_branch
263
257
        # BzrDir.clone_on_transport() uses the result of create_branch but does
275
269
        self._real_bzrdir.destroy_branch(name=name)
276
270
        self._next_open_branch_result = None
277
271
 
278
 
    def create_workingtree(self, revision_id=None, from_branch=None,
279
 
        accelerator_tree=None, hardlink=False):
 
272
    def create_workingtree(self, revision_id=None, from_branch=None):
280
273
        raise errors.NotLocalUrl(self.transport.base)
281
274
 
282
 
    def find_branch_format(self, name=None):
 
275
    def find_branch_format(self):
283
276
        """Find the branch 'format' for this bzrdir.
284
277
 
285
278
        This might be a synthetic object for e.g. RemoteBranch and SVN.
286
279
        """
287
 
        b = self.open_branch(name=name)
 
280
        b = self.open_branch()
288
281
        return b._format
289
282
 
290
 
    def get_branch_reference(self, name=None):
 
283
    def get_branch_reference(self):
291
284
        """See BzrDir.get_branch_reference()."""
292
 
        if name is not None:
293
 
            # XXX JRV20100304: Support opening colocated branches
294
 
            raise errors.NoColocatedBranchSupport(self)
295
285
        response = self._get_branch_reference()
296
286
        if response[0] == 'ref':
297
287
            return response[1]
328
318
            raise errors.UnexpectedSmartServerResponse(response)
329
319
        return response
330
320
 
331
 
    def _get_tree_branch(self, name=None):
 
321
    def _get_tree_branch(self):
332
322
        """See BzrDir._get_tree_branch()."""
333
 
        return None, self.open_branch(name=name)
 
323
        return None, self.open_branch()
334
324
 
335
325
    def open_branch(self, name=None, unsupported=False,
336
326
                    ignore_fallbacks=False):
451
441
        """Upgrading of remote bzrdirs is not supported yet."""
452
442
        return False
453
443
 
454
 
    def needs_format_conversion(self, format):
 
444
    def needs_format_conversion(self, format=None):
455
445
        """Upgrading of remote bzrdirs is not supported yet."""
 
446
        if format is None:
 
447
            symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
 
448
                % 'needs_format_conversion(format=None)')
456
449
        return False
457
450
 
458
451
    def clone(self, url, revision_id=None, force_new_repo=False,
651
644
 
652
645
 
653
646
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
654
 
    controldir.ControlComponent):
 
647
    bzrdir.ControlComponent):
655
648
    """Repository accessed over rpc.
656
649
 
657
650
    For the moment most operations are performed using local transport-backed
902
895
    def _has_same_fallbacks(self, other_repo):
903
896
        """Returns true if the repositories have the same fallbacks."""
904
897
        # XXX: copied from Repository; it should be unified into a base class
905
 
        # <https://bugs.launchpad.net/bzr/+bug/401622>
 
898
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
906
899
        my_fb = self._fallback_repositories
907
900
        other_fb = other_repo._fallback_repositories
908
901
        if len(my_fb) != len(other_fb):
1004
997
        pass
1005
998
 
1006
999
    def lock_read(self):
1007
 
        """Lock the repository for read operations.
1008
 
 
1009
 
        :return: A bzrlib.lock.LogicalLockResult.
1010
 
        """
1011
1000
        # wrong eventually - want a local lock cache context
1012
1001
        if not self._lock_mode:
1013
1002
            self._note_lock('r')
1020
1009
                repo.lock_read()
1021
1010
        else:
1022
1011
            self._lock_count += 1
1023
 
        return lock.LogicalLockResult(self.unlock)
1024
1012
 
1025
1013
    def _remote_lock_write(self, token):
1026
1014
        path = self.bzrdir._path_for_remote_call(self._client)
1066
1054
            raise errors.ReadOnlyError(self)
1067
1055
        else:
1068
1056
            self._lock_count += 1
1069
 
        return RepositoryWriteLockResult(self.unlock, self._lock_token or None)
 
1057
        return self._lock_token or None
1070
1058
 
1071
1059
    def leave_lock_in_place(self):
1072
1060
        if not self._lock_token:
1318
1306
        return self._real_repository.make_working_trees()
1319
1307
 
1320
1308
    def refresh_data(self):
1321
 
        """Re-read any data needed to synchronise with disk.
 
1309
        """Re-read any data needed to to synchronise with disk.
1322
1310
 
1323
1311
        This method is intended to be called after another repository instance
1324
1312
        (such as one used by a smart server) has inserted data into the
1325
 
        repository. On all repositories this will work outside of write groups.
1326
 
        Some repository formats (pack and newer for bzrlib native formats)
1327
 
        support refresh_data inside write groups. If called inside a write
1328
 
        group on a repository that does not support refreshing in a write group
1329
 
        IsInWriteGroupError will be raised.
 
1313
        repository. It may not be called during a write group, but may be
 
1314
        called at any other time.
1330
1315
        """
 
1316
        if self.is_in_write_group():
 
1317
            raise errors.InternalBzrError(
 
1318
                "May not refresh_data while in a write group.")
1331
1319
        if self._real_repository is not None:
1332
1320
            self._real_repository.refresh_data()
1333
1321
 
1345
1333
        return result
1346
1334
 
1347
1335
    @needs_read_lock
1348
 
    def search_missing_revision_ids(self, other,
1349
 
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
1350
 
            find_ghosts=True, revision_ids=None, if_present_ids=None):
 
1336
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
1351
1337
        """Return the revision ids that other has that this does not.
1352
1338
 
1353
1339
        These are returned in topological order.
1354
1340
 
1355
1341
        revision_id: only return revision ids included by revision_id.
1356
1342
        """
1357
 
        if symbol_versioning.deprecated_passed(revision_id):
1358
 
            symbol_versioning.warn(
1359
 
                'search_missing_revision_ids(revision_id=...) was '
1360
 
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
1361
 
                DeprecationWarning, stacklevel=2)
1362
 
            if revision_ids is not None:
1363
 
                raise AssertionError(
1364
 
                    'revision_ids is mutually exclusive with revision_id')
1365
 
            if revision_id is not None:
1366
 
                revision_ids = [revision_id]
1367
 
        inter_repo = repository.InterRepository.get(other, self)
1368
 
        return inter_repo.search_missing_revision_ids(
1369
 
            find_ghosts=find_ghosts, revision_ids=revision_ids,
1370
 
            if_present_ids=if_present_ids)
 
1343
        return repository.InterRepository.get(
 
1344
            other, self).search_missing_revision_ids(revision_id, find_ghosts)
1371
1345
 
1372
 
    def fetch(self, source, revision_id=None, find_ghosts=False,
 
1346
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
1373
1347
            fetch_spec=None):
1374
1348
        # No base implementation to use as RemoteRepository is not a subclass
1375
1349
        # of Repository; so this is a copy of Repository.fetch().
1393
1367
        # the InterRepository base class, which raises an
1394
1368
        # IncompatibleRepositories when asked to fetch.
1395
1369
        inter = repository.InterRepository.get(source, self)
1396
 
        return inter.fetch(revision_id=revision_id,
 
1370
        return inter.fetch(revision_id=revision_id, pb=pb,
1397
1371
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
1398
1372
 
1399
1373
    def create_bundle(self, target, base, fileobj, format=None):
1774
1748
        return '\n'.join((start_keys, stop_keys, count))
1775
1749
 
1776
1750
    def _serialise_search_result(self, search_result):
1777
 
        parts = search_result.get_network_struct()
 
1751
        if isinstance(search_result, graph.PendingAncestryResult):
 
1752
            parts = ['ancestry-of']
 
1753
            parts.extend(search_result.heads)
 
1754
        else:
 
1755
            recipe = search_result.get_recipe()
 
1756
            parts = [recipe[0], self._serialise_search_recipe(recipe)]
1778
1757
        return '\n'.join(parts)
1779
1758
 
1780
1759
    def autopack(self):
1974
1953
        candidate_verbs = [
1975
1954
            ('Repository.get_stream_1.19', (1, 19)),
1976
1955
            ('Repository.get_stream', (1, 13))]
1977
 
 
1978
1956
        found_verb = False
1979
1957
        for verb, version in candidate_verbs:
1980
1958
            if medium._is_remote_before(version):
1984
1962
                    verb, args, search_bytes)
1985
1963
            except errors.UnknownSmartMethod:
1986
1964
                medium._remember_remote_is_before(version)
1987
 
            except errors.UnknownErrorFromSmartServer, e:
1988
 
                if isinstance(search, graph.EverythingResult):
1989
 
                    error_verb = e.error_from_smart_server.error_verb
1990
 
                    if error_verb == 'BadSearch':
1991
 
                        # Pre-2.4 servers don't support this sort of search.
1992
 
                        # XXX: perhaps falling back to VFS on BadSearch is a
1993
 
                        # good idea in general?  It might provide a little bit
1994
 
                        # of protection against client-side bugs.
1995
 
                        medium._remember_remote_is_before((2, 4))
1996
 
                        break
1997
 
                raise
1998
1965
            else:
1999
1966
                response_tuple, response_handler = response
2000
1967
                found_verb = True
2004
1971
        if response_tuple[0] != 'ok':
2005
1972
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2006
1973
        byte_stream = response_handler.read_streamed_body()
2007
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
2008
 
            self._record_counter)
 
1974
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
2009
1975
        if src_format.network_name() != repo._format.network_name():
2010
1976
            raise AssertionError(
2011
1977
                "Mismatched RemoteRepository and stream src %r, %r" % (
2115
2081
                                  name=name)
2116
2082
        return result
2117
2083
 
2118
 
    def initialize(self, a_bzrdir, name=None, repository=None):
 
2084
    def initialize(self, a_bzrdir, name=None):
2119
2085
        # 1) get the network name to use.
2120
2086
        if self._custom_format:
2121
2087
            network_name = self._custom_format.network_name()
2149
2115
        # Turn the response into a RemoteRepository object.
2150
2116
        format = RemoteBranchFormat(network_name=response[1])
2151
2117
        repo_format = response_tuple_to_repo_format(response[3:])
2152
 
        repo_path = response[2]
2153
 
        if repository is not None:
2154
 
            remote_repo_url = urlutils.join(medium.base, repo_path)
2155
 
            url_diff = urlutils.relative_url(repository.user_url,
2156
 
                    remote_repo_url)
2157
 
            if url_diff != '.':
2158
 
                raise AssertionError(
2159
 
                    'repository.user_url %r does not match URL from server '
2160
 
                    'response (%r + %r)'
2161
 
                    % (repository.user_url, medium.base, repo_path))
2162
 
            remote_repo = repository
 
2118
        if response[2] == '':
 
2119
            repo_bzrdir = a_bzrdir
2163
2120
        else:
2164
 
            if repo_path == '':
2165
 
                repo_bzrdir = a_bzrdir
2166
 
            else:
2167
 
                repo_bzrdir = RemoteBzrDir(
2168
 
                    a_bzrdir.root_transport.clone(repo_path), a_bzrdir._format,
2169
 
                    a_bzrdir._client)
2170
 
            remote_repo = RemoteRepository(repo_bzrdir, repo_format)
 
2121
            repo_bzrdir = RemoteBzrDir(
 
2122
                a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
 
2123
                a_bzrdir._client)
 
2124
        remote_repo = RemoteRepository(repo_bzrdir, repo_format)
2171
2125
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
2172
2126
            format=format, setup_stacking=False, name=name)
2173
2127
        # XXX: We know this is a new branch, so it must have revno 0, revid
2194
2148
        self._ensure_real()
2195
2149
        return self._custom_format.supports_set_append_revisions_only()
2196
2150
 
2197
 
    def _native_heads_to_fetch(self):
2198
 
        # If the branch format is a metadir format *and* its heads_to_fetch
2199
 
        # implementation is not overridden vs the base class, we can use the
2200
 
        # base class logic rather than use the heads_to_fetch RPC.  This is
2201
 
        # usually free in terms of net round trips, as the last-revision and
2202
 
        # tags info fetched is cached and would be fetched anyway.
2203
 
        self._ensure_real()
2204
 
        if isinstance(self._custom_format, branch.BranchFormatMetadir):
2205
 
            branch_class = self._custom_format._branch_class()
2206
 
            heads_to_fetch_impl = branch_class.heads_to_fetch.im_func
2207
 
            if heads_to_fetch_impl is branch.Branch.heads_to_fetch.im_func:
2208
 
                return True
2209
 
        return False
2210
2151
 
2211
2152
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
2212
2153
    """Branch stored on a server accessed by HPSS RPC.
2416
2357
        self._ensure_real()
2417
2358
        return self._real_branch._get_tags_bytes()
2418
2359
 
2419
 
    @needs_read_lock
2420
2360
    def _get_tags_bytes(self):
2421
 
        if self._tags_bytes is None:
2422
 
            self._tags_bytes = self._get_tags_bytes_via_hpss()
2423
 
        return self._tags_bytes
2424
 
 
2425
 
    def _get_tags_bytes_via_hpss(self):
2426
2361
        medium = self._client._medium
2427
2362
        if medium._is_remote_before((1, 13)):
2428
2363
            return self._vfs_get_tags_bytes()
2438
2373
        return self._real_branch._set_tags_bytes(bytes)
2439
2374
 
2440
2375
    def _set_tags_bytes(self, bytes):
2441
 
        if self.is_locked():
2442
 
            self._tags_bytes = bytes
2443
2376
        medium = self._client._medium
2444
2377
        if medium._is_remote_before((1, 18)):
2445
2378
            self._vfs_set_tags_bytes(bytes)
2454
2387
            self._vfs_set_tags_bytes(bytes)
2455
2388
 
2456
2389
    def lock_read(self):
2457
 
        """Lock the branch for read operations.
2458
 
 
2459
 
        :return: A bzrlib.lock.LogicalLockResult.
2460
 
        """
2461
2390
        self.repository.lock_read()
2462
2391
        if not self._lock_mode:
2463
2392
            self._note_lock('r')
2467
2396
                self._real_branch.lock_read()
2468
2397
        else:
2469
2398
            self._lock_count += 1
2470
 
        return lock.LogicalLockResult(self.unlock)
2471
2399
 
2472
2400
    def _remote_lock_write(self, token):
2473
2401
        if token is None:
2474
2402
            branch_token = repo_token = ''
2475
2403
        else:
2476
2404
            branch_token = token
2477
 
            repo_token = self.repository.lock_write().repository_token
 
2405
            repo_token = self.repository.lock_write()
2478
2406
            self.repository.unlock()
2479
2407
        err_context = {'token': token}
2480
 
        try:
2481
 
            response = self._call(
2482
 
                'Branch.lock_write', self._remote_path(), branch_token,
2483
 
                repo_token or '', **err_context)
2484
 
        except errors.LockContention, e:
2485
 
            # The LockContention from the server doesn't have any
2486
 
            # information about the lock_url. We re-raise LockContention
2487
 
            # with valid lock_url.
2488
 
            raise errors.LockContention('(remote lock)',
2489
 
                self.repository.base.split('.bzr/')[0])
 
2408
        response = self._call(
 
2409
            'Branch.lock_write', self._remote_path(), branch_token,
 
2410
            repo_token or '', **err_context)
2490
2411
        if response[0] != 'ok':
2491
2412
            raise errors.UnexpectedSmartServerResponse(response)
2492
2413
        ok, branch_token, repo_token = response
2513
2434
            self._lock_mode = 'w'
2514
2435
            self._lock_count = 1
2515
2436
        elif self._lock_mode == 'r':
2516
 
            raise errors.ReadOnlyError(self)
 
2437
            raise errors.ReadOnlyTransaction
2517
2438
        else:
2518
2439
            if token is not None:
2519
2440
                # A token was given to lock_write, and we're relocking, so
2524
2445
            self._lock_count += 1
2525
2446
            # Re-lock the repository too.
2526
2447
            self.repository.lock_write(self._repo_lock_token)
2527
 
        return BranchWriteLockResult(self.unlock, self._lock_token or None)
 
2448
        return self._lock_token or None
2528
2449
 
2529
2450
    def _unlock(self, branch_token, repo_token):
2530
2451
        err_context = {'token': str((branch_token, repo_token))}
2794
2715
        self._ensure_real()
2795
2716
        return self._real_branch.set_push_location(location)
2796
2717
 
2797
 
    def heads_to_fetch(self):
2798
 
        if self._format._native_heads_to_fetch():
2799
 
            return branch.Branch.heads_to_fetch(self)
2800
 
        medium = self._client._medium
2801
 
        if medium._is_remote_before((2, 4)):
2802
 
            return self._vfs_heads_to_fetch()
2803
 
        try:
2804
 
            return self._rpc_heads_to_fetch()
2805
 
        except errors.UnknownSmartMethod:
2806
 
            medium._remember_remote_is_before((2, 4))
2807
 
            return self._vfs_heads_to_fetch()
2808
 
 
2809
 
    def _rpc_heads_to_fetch(self):
2810
 
        response = self._call('Branch.heads_to_fetch', self._remote_path())
2811
 
        if len(response) != 2:
2812
 
            raise errors.UnexpectedSmartServerResponse(response)
2813
 
        must_fetch, if_present_fetch = response
2814
 
        return set(must_fetch), set(if_present_fetch)
2815
 
 
2816
 
    def _vfs_heads_to_fetch(self):
2817
 
        self._ensure_real()
2818
 
        return self._real_branch.heads_to_fetch()
2819
 
 
2820
2718
 
2821
2719
class RemoteConfig(object):
2822
2720
    """A Config that reads and writes from smart verbs.
2876
2774
        medium = self._branch._client._medium
2877
2775
        if medium._is_remote_before((1, 14)):
2878
2776
            return self._vfs_set_option(value, name, section)
2879
 
        if isinstance(value, dict):
2880
 
            if medium._is_remote_before((2, 2)):
2881
 
                return self._vfs_set_option(value, name, section)
2882
 
            return self._set_config_option_dict(value, name, section)
2883
 
        else:
2884
 
            return self._set_config_option(value, name, section)
2885
 
 
2886
 
    def _set_config_option(self, value, name, section):
2887
2777
        try:
2888
2778
            path = self._branch._remote_path()
2889
2779
            response = self._branch._client.call('Branch.set_config_option',
2890
2780
                path, self._branch._lock_token, self._branch._repo_lock_token,
2891
2781
                value.encode('utf8'), name, section or '')
2892
2782
        except errors.UnknownSmartMethod:
2893
 
            medium = self._branch._client._medium
2894
2783
            medium._remember_remote_is_before((1, 14))
2895
2784
            return self._vfs_set_option(value, name, section)
2896
2785
        if response != ():
2897
2786
            raise errors.UnexpectedSmartServerResponse(response)
2898
2787
 
2899
 
    def _serialize_option_dict(self, option_dict):
2900
 
        utf8_dict = {}
2901
 
        for key, value in option_dict.items():
2902
 
            if isinstance(key, unicode):
2903
 
                key = key.encode('utf8')
2904
 
            if isinstance(value, unicode):
2905
 
                value = value.encode('utf8')
2906
 
            utf8_dict[key] = value
2907
 
        return bencode.bencode(utf8_dict)
2908
 
 
2909
 
    def _set_config_option_dict(self, value, name, section):
2910
 
        try:
2911
 
            path = self._branch._remote_path()
2912
 
            serialised_dict = self._serialize_option_dict(value)
2913
 
            response = self._branch._client.call(
2914
 
                'Branch.set_config_option_dict',
2915
 
                path, self._branch._lock_token, self._branch._repo_lock_token,
2916
 
                serialised_dict, name, section or '')
2917
 
        except errors.UnknownSmartMethod:
2918
 
            medium = self._branch._client._medium
2919
 
            medium._remember_remote_is_before((2, 2))
2920
 
            return self._vfs_set_option(value, name, section)
2921
 
        if response != ():
2922
 
            raise errors.UnexpectedSmartServerResponse(response)
2923
 
 
2924
2788
    def _real_object(self):
2925
2789
        self._branch._ensure_real()
2926
2790
        return self._branch._real_branch