/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
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
from __future__ import absolute_import
18
 
 
19
17
import bz2
20
 
import zlib
21
18
 
22
19
from bzrlib import (
23
20
    bencode,
24
21
    branch,
25
 
    bzrdir as _mod_bzrdir,
 
22
    bzrdir,
26
23
    config,
27
 
    controldir,
28
24
    debug,
29
25
    errors,
30
 
    gpg,
31
26
    graph,
32
 
    inventory_delta,
33
27
    lock,
34
28
    lockdir,
35
 
    osutils,
36
 
    registry,
 
29
    repository,
37
30
    repository as _mod_repository,
 
31
    revision,
38
32
    revision as _mod_revision,
39
33
    static_tuple,
40
34
    symbol_versioning,
41
 
    testament as _mod_testament,
42
 
    urlutils,
43
 
    vf_repository,
44
 
    vf_search,
45
 
    )
46
 
from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult
 
35
)
 
36
from bzrlib.branch import BranchReferenceFormat
 
37
from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat
47
38
from bzrlib.decorators import needs_read_lock, needs_write_lock, only_raises
48
39
from bzrlib.errors import (
49
40
    NoSuchRevision,
50
41
    SmartProtocolError,
51
42
    )
52
 
from bzrlib.i18n import gettext
53
 
from bzrlib.inventory import Inventory
54
43
from bzrlib.lockable_files import LockableFiles
55
44
from bzrlib.smart import client, vfs, repository as smart_repo
56
 
from bzrlib.smart.client import _SmartClient
57
 
from bzrlib.revision import NULL_REVISION
58
 
from bzrlib.revisiontree import InventoryRevisionTree
59
 
from bzrlib.repository import RepositoryWriteLockResult, _LazyListJoin
60
 
from bzrlib.serializer import format_registry as serializer_format_registry
61
 
from bzrlib.trace import mutter, note, warning, log_exception_quietly
62
 
 
63
 
 
64
 
_DEFAULT_SEARCH_DEPTH = 100
 
45
from bzrlib.revision import ensure_null, NULL_REVISION
 
46
from bzrlib.trace import mutter, note, warning
65
47
 
66
48
 
67
49
class _RpcHelper(object):
104
86
    return format
105
87
 
106
88
 
107
 
# Note that RemoteBzrDirProber lives in bzrlib.bzrdir so bzrlib.remote
108
 
# does not have to be imported unless a remote format is involved.
109
 
 
110
 
class RemoteBzrDirFormat(_mod_bzrdir.BzrDirMetaFormat1):
111
 
    """Format representing bzrdirs accessed via a smart server"""
112
 
 
113
 
    supports_workingtrees = False
114
 
 
115
 
    def __init__(self):
116
 
        _mod_bzrdir.BzrDirMetaFormat1.__init__(self)
117
 
        # XXX: It's a bit ugly that the network name is here, because we'd
118
 
        # like to believe that format objects are stateless or at least
119
 
        # immutable,  However, we do at least avoid mutating the name after
120
 
        # it's returned.  See <https://bugs.launchpad.net/bzr/+bug/504102>
121
 
        self._network_name = None
122
 
 
123
 
    def __repr__(self):
124
 
        return "%s(_network_name=%r)" % (self.__class__.__name__,
125
 
            self._network_name)
126
 
 
127
 
    def get_format_description(self):
128
 
        if self._network_name:
129
 
            try:
130
 
                real_format = controldir.network_format_registry.get(
131
 
                        self._network_name)
132
 
            except KeyError:
133
 
                pass
134
 
            else:
135
 
                return 'Remote: ' + real_format.get_format_description()
136
 
        return 'bzr remote bzrdir'
137
 
 
138
 
    def get_format_string(self):
139
 
        raise NotImplementedError(self.get_format_string)
140
 
 
141
 
    def network_name(self):
142
 
        if self._network_name:
143
 
            return self._network_name
144
 
        else:
145
 
            raise AssertionError("No network name set.")
146
 
 
147
 
    def initialize_on_transport(self, transport):
148
 
        try:
149
 
            # hand off the request to the smart server
150
 
            client_medium = transport.get_smart_medium()
151
 
        except errors.NoSmartMedium:
152
 
            # TODO: lookup the local format from a server hint.
153
 
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
154
 
            return local_dir_format.initialize_on_transport(transport)
155
 
        client = _SmartClient(client_medium)
156
 
        path = client.remote_path_from_transport(transport)
157
 
        try:
158
 
            response = client.call('BzrDirFormat.initialize', path)
159
 
        except errors.ErrorFromSmartServer, err:
160
 
            _translate_error(err, path=path)
161
 
        if response[0] != 'ok':
162
 
            raise errors.SmartProtocolError('unexpected response code %s' % (response,))
163
 
        format = RemoteBzrDirFormat()
164
 
        self._supply_sub_formats_to(format)
165
 
        return RemoteBzrDir(transport, format)
166
 
 
167
 
    def parse_NoneTrueFalse(self, arg):
168
 
        if not arg:
169
 
            return None
170
 
        if arg == 'False':
171
 
            return False
172
 
        if arg == 'True':
173
 
            return True
174
 
        raise AssertionError("invalid arg %r" % arg)
175
 
 
176
 
    def _serialize_NoneTrueFalse(self, arg):
177
 
        if arg is False:
178
 
            return 'False'
179
 
        if arg:
180
 
            return 'True'
181
 
        return ''
182
 
 
183
 
    def _serialize_NoneString(self, arg):
184
 
        return arg or ''
185
 
 
186
 
    def initialize_on_transport_ex(self, transport, use_existing_dir=False,
187
 
        create_prefix=False, force_new_repo=False, stacked_on=None,
188
 
        stack_on_pwd=None, repo_format_name=None, make_working_trees=None,
189
 
        shared_repo=False):
190
 
        try:
191
 
            # hand off the request to the smart server
192
 
            client_medium = transport.get_smart_medium()
193
 
        except errors.NoSmartMedium:
194
 
            do_vfs = True
195
 
        else:
196
 
            # Decline to open it if the server doesn't support our required
197
 
            # version (3) so that the VFS-based transport will do it.
198
 
            if client_medium.should_probe():
199
 
                try:
200
 
                    server_version = client_medium.protocol_version()
201
 
                    if server_version != '2':
202
 
                        do_vfs = True
203
 
                    else:
204
 
                        do_vfs = False
205
 
                except errors.SmartProtocolError:
206
 
                    # Apparently there's no usable smart server there, even though
207
 
                    # the medium supports the smart protocol.
208
 
                    do_vfs = True
209
 
            else:
210
 
                do_vfs = False
211
 
        if not do_vfs:
212
 
            client = _SmartClient(client_medium)
213
 
            path = client.remote_path_from_transport(transport)
214
 
            if client_medium._is_remote_before((1, 16)):
215
 
                do_vfs = True
216
 
        if do_vfs:
217
 
            # TODO: lookup the local format from a server hint.
218
 
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
219
 
            self._supply_sub_formats_to(local_dir_format)
220
 
            return local_dir_format.initialize_on_transport_ex(transport,
221
 
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
222
 
                force_new_repo=force_new_repo, stacked_on=stacked_on,
223
 
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
224
 
                make_working_trees=make_working_trees, shared_repo=shared_repo,
225
 
                vfs_only=True)
226
 
        return self._initialize_on_transport_ex_rpc(client, path, transport,
227
 
            use_existing_dir, create_prefix, force_new_repo, stacked_on,
228
 
            stack_on_pwd, repo_format_name, make_working_trees, shared_repo)
229
 
 
230
 
    def _initialize_on_transport_ex_rpc(self, client, path, transport,
231
 
        use_existing_dir, create_prefix, force_new_repo, stacked_on,
232
 
        stack_on_pwd, repo_format_name, make_working_trees, shared_repo):
233
 
        args = []
234
 
        args.append(self._serialize_NoneTrueFalse(use_existing_dir))
235
 
        args.append(self._serialize_NoneTrueFalse(create_prefix))
236
 
        args.append(self._serialize_NoneTrueFalse(force_new_repo))
237
 
        args.append(self._serialize_NoneString(stacked_on))
238
 
        # stack_on_pwd is often/usually our transport
239
 
        if stack_on_pwd:
240
 
            try:
241
 
                stack_on_pwd = transport.relpath(stack_on_pwd)
242
 
                if not stack_on_pwd:
243
 
                    stack_on_pwd = '.'
244
 
            except errors.PathNotChild:
245
 
                pass
246
 
        args.append(self._serialize_NoneString(stack_on_pwd))
247
 
        args.append(self._serialize_NoneString(repo_format_name))
248
 
        args.append(self._serialize_NoneTrueFalse(make_working_trees))
249
 
        args.append(self._serialize_NoneTrueFalse(shared_repo))
250
 
        request_network_name = self._network_name or \
251
 
            _mod_bzrdir.BzrDirFormat.get_default_format().network_name()
252
 
        try:
253
 
            response = client.call('BzrDirFormat.initialize_ex_1.16',
254
 
                request_network_name, path, *args)
255
 
        except errors.UnknownSmartMethod:
256
 
            client._medium._remember_remote_is_before((1,16))
257
 
            local_dir_format = _mod_bzrdir.BzrDirMetaFormat1()
258
 
            self._supply_sub_formats_to(local_dir_format)
259
 
            return local_dir_format.initialize_on_transport_ex(transport,
260
 
                use_existing_dir=use_existing_dir, create_prefix=create_prefix,
261
 
                force_new_repo=force_new_repo, stacked_on=stacked_on,
262
 
                stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
263
 
                make_working_trees=make_working_trees, shared_repo=shared_repo,
264
 
                vfs_only=True)
265
 
        except errors.ErrorFromSmartServer, err:
266
 
            _translate_error(err, path=path)
267
 
        repo_path = response[0]
268
 
        bzrdir_name = response[6]
269
 
        require_stacking = response[7]
270
 
        require_stacking = self.parse_NoneTrueFalse(require_stacking)
271
 
        format = RemoteBzrDirFormat()
272
 
        format._network_name = bzrdir_name
273
 
        self._supply_sub_formats_to(format)
274
 
        bzrdir = RemoteBzrDir(transport, format, _client=client)
275
 
        if repo_path:
276
 
            repo_format = response_tuple_to_repo_format(response[1:])
277
 
            if repo_path == '.':
278
 
                repo_path = ''
279
 
            if repo_path:
280
 
                repo_bzrdir_format = RemoteBzrDirFormat()
281
 
                repo_bzrdir_format._network_name = response[5]
282
 
                repo_bzr = RemoteBzrDir(transport.clone(repo_path),
283
 
                    repo_bzrdir_format)
284
 
            else:
285
 
                repo_bzr = bzrdir
286
 
            final_stack = response[8] or None
287
 
            final_stack_pwd = response[9] or None
288
 
            if final_stack_pwd:
289
 
                final_stack_pwd = urlutils.join(
290
 
                    transport.base, final_stack_pwd)
291
 
            remote_repo = RemoteRepository(repo_bzr, repo_format)
292
 
            if len(response) > 10:
293
 
                # Updated server verb that locks remotely.
294
 
                repo_lock_token = response[10] or None
295
 
                remote_repo.lock_write(repo_lock_token, _skip_rpc=True)
296
 
                if repo_lock_token:
297
 
                    remote_repo.dont_leave_lock_in_place()
298
 
            else:
299
 
                remote_repo.lock_write()
300
 
            policy = _mod_bzrdir.UseExistingRepository(remote_repo, final_stack,
301
 
                final_stack_pwd, require_stacking)
302
 
            policy.acquire_repository()
303
 
        else:
304
 
            remote_repo = None
305
 
            policy = None
306
 
        bzrdir._format.set_branch_format(self.get_branch_format())
307
 
        if require_stacking:
308
 
            # The repo has already been created, but we need to make sure that
309
 
            # we'll make a stackable branch.
310
 
            bzrdir._format.require_stacking(_skip_repo=True)
311
 
        return remote_repo, bzrdir, require_stacking, policy
312
 
 
313
 
    def _open(self, transport):
314
 
        return RemoteBzrDir(transport, self)
315
 
 
316
 
    def __eq__(self, other):
317
 
        if not isinstance(other, RemoteBzrDirFormat):
318
 
            return False
319
 
        return self.get_format_description() == other.get_format_description()
320
 
 
321
 
    def __return_repository_format(self):
322
 
        # Always return a RemoteRepositoryFormat object, but if a specific bzr
323
 
        # repository format has been asked for, tell the RemoteRepositoryFormat
324
 
        # that it should use that for init() etc.
325
 
        result = RemoteRepositoryFormat()
326
 
        custom_format = getattr(self, '_repository_format', None)
327
 
        if custom_format:
328
 
            if isinstance(custom_format, RemoteRepositoryFormat):
329
 
                return custom_format
330
 
            else:
331
 
                # We will use the custom format to create repositories over the
332
 
                # wire; expose its details like rich_root_data for code to
333
 
                # query
334
 
                result._custom_format = custom_format
335
 
        return result
336
 
 
337
 
    def get_branch_format(self):
338
 
        result = _mod_bzrdir.BzrDirMetaFormat1.get_branch_format(self)
339
 
        if not isinstance(result, RemoteBranchFormat):
340
 
            new_result = RemoteBranchFormat()
341
 
            new_result._custom_format = result
342
 
            # cache the result
343
 
            self.set_branch_format(new_result)
344
 
            result = new_result
345
 
        return result
346
 
 
347
 
    repository_format = property(__return_repository_format,
348
 
        _mod_bzrdir.BzrDirMetaFormat1._set_repository_format) #.im_func)
349
 
 
350
 
 
351
 
class RemoteControlStore(config.IniFileStore):
352
 
    """Control store which attempts to use HPSS calls to retrieve control store.
353
 
 
354
 
    Note that this is specific to bzr-based formats.
355
 
    """
356
 
 
357
 
    def __init__(self, bzrdir):
358
 
        super(RemoteControlStore, self).__init__()
359
 
        self.bzrdir = bzrdir
360
 
        self._real_store = None
361
 
 
362
 
    def lock_write(self, token=None):
363
 
        self._ensure_real()
364
 
        return self._real_store.lock_write(token)
365
 
 
366
 
    def unlock(self):
367
 
        self._ensure_real()
368
 
        return self._real_store.unlock()
369
 
 
370
 
    @needs_write_lock
371
 
    def save(self):
372
 
        # We need to be able to override the undecorated implementation
373
 
        self.save_without_locking()
374
 
 
375
 
    def save_without_locking(self):
376
 
        super(RemoteControlStore, self).save()
377
 
 
378
 
    def _ensure_real(self):
379
 
        self.bzrdir._ensure_real()
380
 
        if self._real_store is None:
381
 
            self._real_store = config.ControlStore(self.bzrdir)
382
 
 
383
 
    def external_url(self):
384
 
        return self.bzrdir.user_url
385
 
 
386
 
    def _load_content(self):
387
 
        medium = self.bzrdir._client._medium
388
 
        path = self.bzrdir._path_for_remote_call(self.bzrdir._client)
389
 
        try:
390
 
            response, handler = self.bzrdir._call_expecting_body(
391
 
                'BzrDir.get_config_file', path)
392
 
        except errors.UnknownSmartMethod:
393
 
            self._ensure_real()
394
 
            return self._real_store._load_content()
395
 
        if len(response) and response[0] != 'ok':
396
 
            raise errors.UnexpectedSmartServerResponse(response)
397
 
        return handler.read_body_bytes()
398
 
 
399
 
    def _save_content(self, content):
400
 
        # FIXME JRV 2011-11-22: Ideally this should use a
401
 
        # HPSS call too, but at the moment it is not possible
402
 
        # to write lock control directories.
403
 
        self._ensure_real()
404
 
        return self._real_store._save_content(content)
405
 
 
406
 
 
407
 
class RemoteBzrDir(_mod_bzrdir.BzrDir, _RpcHelper):
 
89
# Note: RemoteBzrDirFormat is in bzrdir.py
 
90
 
 
91
class RemoteBzrDir(BzrDir, _RpcHelper):
408
92
    """Control directory on a remote server, accessed via bzr:// or similar."""
409
93
 
410
94
    def __init__(self, transport, format, _client=None, _force_probe=False):
413
97
        :param _client: Private parameter for testing. Disables probing and the
414
98
            use of a real bzrdir.
415
99
        """
416
 
        _mod_bzrdir.BzrDir.__init__(self, transport, format)
 
100
        BzrDir.__init__(self, transport, format)
417
101
        # this object holds a delegated bzrdir that uses file-level operations
418
102
        # to talk to the other side
419
103
        self._real_bzrdir = None
479
163
                import traceback
480
164
                warning('VFS BzrDir access triggered\n%s',
481
165
                    ''.join(traceback.format_stack()))
482
 
            self._real_bzrdir = _mod_bzrdir.BzrDir.open_from_transport(
 
166
            self._real_bzrdir = BzrDir.open_from_transport(
483
167
                self.root_transport, _server_formats=False)
484
168
            self._format._network_name = \
485
169
                self._real_bzrdir._format.network_name()
491
175
        # Prevent aliasing problems in the next_open_branch_result cache.
492
176
        # See create_branch for rationale.
493
177
        self._next_open_branch_result = None
494
 
        return _mod_bzrdir.BzrDir.break_lock(self)
495
 
 
496
 
    def _vfs_checkout_metadir(self):
497
 
        self._ensure_real()
498
 
        return self._real_bzrdir.checkout_metadir()
499
 
 
500
 
    def checkout_metadir(self):
501
 
        """Retrieve the controldir format to use for checkouts of this one.
502
 
        """
503
 
        medium = self._client._medium
504
 
        if medium._is_remote_before((2, 5)):
505
 
            return self._vfs_checkout_metadir()
506
 
        path = self._path_for_remote_call(self._client)
507
 
        try:
508
 
            response = self._client.call('BzrDir.checkout_metadir',
509
 
                path)
510
 
        except errors.UnknownSmartMethod:
511
 
            medium._remember_remote_is_before((2, 5))
512
 
            return self._vfs_checkout_metadir()
513
 
        if len(response) != 3:
514
 
            raise errors.UnexpectedSmartServerResponse(response)
515
 
        control_name, repo_name, branch_name = response
516
 
        try:
517
 
            format = controldir.network_format_registry.get(control_name)
518
 
        except KeyError:
519
 
            raise errors.UnknownFormatError(kind='control',
520
 
                format=control_name)
521
 
        if repo_name:
522
 
            try:
523
 
                repo_format = _mod_repository.network_format_registry.get(
524
 
                    repo_name)
525
 
            except KeyError:
526
 
                raise errors.UnknownFormatError(kind='repository',
527
 
                    format=repo_name)
528
 
            format.repository_format = repo_format
529
 
        if branch_name:
530
 
            try:
531
 
                format.set_branch_format(
532
 
                    branch.network_format_registry.get(branch_name))
533
 
            except KeyError:
534
 
                raise errors.UnknownFormatError(kind='branch',
535
 
                    format=branch_name)
536
 
        return format
 
178
        return BzrDir.break_lock(self)
537
179
 
538
180
    def _vfs_cloning_metadir(self, require_stacking=False):
539
181
        self._ensure_real()
570
212
        if len(branch_info) != 2:
571
213
            raise errors.UnexpectedSmartServerResponse(response)
572
214
        branch_ref, branch_name = branch_info
573
 
        try:
574
 
            format = controldir.network_format_registry.get(control_name)
575
 
        except KeyError:
576
 
            raise errors.UnknownFormatError(kind='control', format=control_name)
577
 
 
 
215
        format = bzrdir.network_format_registry.get(control_name)
578
216
        if repo_name:
579
 
            try:
580
 
                format.repository_format = _mod_repository.network_format_registry.get(
581
 
                    repo_name)
582
 
            except KeyError:
583
 
                raise errors.UnknownFormatError(kind='repository',
584
 
                    format=repo_name)
 
217
            format.repository_format = repository.network_format_registry.get(
 
218
                repo_name)
585
219
        if branch_ref == 'ref':
586
220
            # XXX: we need possible_transports here to avoid reopening the
587
221
            # connection to the referenced location
588
 
            ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
 
222
            ref_bzrdir = BzrDir.open(branch_name)
589
223
            branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
590
224
            format.set_branch_format(branch_format)
591
225
        elif branch_ref == 'branch':
592
226
            if branch_name:
593
 
                try:
594
 
                    branch_format = branch.network_format_registry.get(
595
 
                        branch_name)
596
 
                except KeyError:
597
 
                    raise errors.UnknownFormatError(kind='branch',
598
 
                        format=branch_name)
599
 
                format.set_branch_format(branch_format)
 
227
                format.set_branch_format(
 
228
                    branch.network_format_registry.get(branch_name))
600
229
        else:
601
230
            raise errors.UnexpectedSmartServerResponse(response)
602
231
        return format
612
241
 
613
242
    def destroy_repository(self):
614
243
        """See BzrDir.destroy_repository"""
615
 
        path = self._path_for_remote_call(self._client)
616
 
        try:
617
 
            response = self._call('BzrDir.destroy_repository', path)
618
 
        except errors.UnknownSmartMethod:
619
 
            self._ensure_real()
620
 
            self._real_bzrdir.destroy_repository()
621
 
            return
622
 
        if response[0] != 'ok':
623
 
            raise SmartProtocolError('unexpected response code %s' % (response,))
 
244
        self._ensure_real()
 
245
        self._real_bzrdir.destroy_repository()
624
246
 
625
 
    def create_branch(self, name=None, repository=None,
626
 
                      append_revisions_only=None):
 
247
    def create_branch(self, name=None):
627
248
        # as per meta1 formats - just delegate to the format object which may
628
249
        # be parameterised.
629
250
        real_branch = self._format.get_branch_format().initialize(self,
630
 
            name=name, repository=repository,
631
 
            append_revisions_only=append_revisions_only)
 
251
            name=name)
632
252
        if not isinstance(real_branch, RemoteBranch):
633
 
            if not isinstance(repository, RemoteRepository):
634
 
                raise AssertionError(
635
 
                    'need a RemoteRepository to use with RemoteBranch, got %r'
636
 
                    % (repository,))
637
 
            result = RemoteBranch(self, repository, real_branch, name=name)
 
253
            result = RemoteBranch(self, self.find_repository(), real_branch,
 
254
                                  name=name)
638
255
        else:
639
256
            result = real_branch
640
257
        # BzrDir.clone_on_transport() uses the result of create_branch but does
648
265
 
649
266
    def destroy_branch(self, name=None):
650
267
        """See BzrDir.destroy_branch"""
651
 
        path = self._path_for_remote_call(self._client)
652
 
        try:
653
 
            if name is not None:
654
 
                args = (name, )
655
 
            else:
656
 
                args = ()
657
 
            response = self._call('BzrDir.destroy_branch', path, *args)
658
 
        except errors.UnknownSmartMethod:
659
 
            self._ensure_real()
660
 
            self._real_bzrdir.destroy_branch(name=name)
661
 
            self._next_open_branch_result = None
662
 
            return
 
268
        self._ensure_real()
 
269
        self._real_bzrdir.destroy_branch(name=name)
663
270
        self._next_open_branch_result = None
664
 
        if response[0] != 'ok':
665
 
            raise SmartProtocolError('unexpected response code %s' % (response,))
666
271
 
667
 
    def create_workingtree(self, revision_id=None, from_branch=None,
668
 
        accelerator_tree=None, hardlink=False):
 
272
    def create_workingtree(self, revision_id=None, from_branch=None):
669
273
        raise errors.NotLocalUrl(self.transport.base)
670
274
 
671
 
    def find_branch_format(self, name=None):
 
275
    def find_branch_format(self):
672
276
        """Find the branch 'format' for this bzrdir.
673
277
 
674
278
        This might be a synthetic object for e.g. RemoteBranch and SVN.
675
279
        """
676
 
        b = self.open_branch(name=name)
 
280
        b = self.open_branch()
677
281
        return b._format
678
282
 
679
 
    def get_branch_reference(self, name=None):
 
283
    def get_branch_reference(self):
680
284
        """See BzrDir.get_branch_reference()."""
681
 
        if name is not None:
682
 
            # XXX JRV20100304: Support opening colocated branches
683
 
            raise errors.NoColocatedBranchSupport(self)
684
285
        response = self._get_branch_reference()
685
286
        if response[0] == 'ref':
686
287
            return response[1]
717
318
            raise errors.UnexpectedSmartServerResponse(response)
718
319
        return response
719
320
 
720
 
    def _get_tree_branch(self, name=None):
 
321
    def _get_tree_branch(self):
721
322
        """See BzrDir._get_tree_branch()."""
722
 
        return None, self.open_branch(name=name)
 
323
        return None, self.open_branch()
723
324
 
724
325
    def open_branch(self, name=None, unsupported=False,
725
 
                    ignore_fallbacks=False, possible_transports=None):
 
326
                    ignore_fallbacks=False):
726
327
        if unsupported:
727
328
            raise NotImplementedError('unsupported flag support not implemented yet.')
728
329
        if self._next_open_branch_result is not None:
735
336
            # a branch reference, use the existing BranchReference logic.
736
337
            format = BranchReferenceFormat()
737
338
            return format.open(self, name=name, _found=True,
738
 
                location=response[1], ignore_fallbacks=ignore_fallbacks,
739
 
                possible_transports=possible_transports)
 
339
                location=response[1], ignore_fallbacks=ignore_fallbacks)
740
340
        branch_format_name = response[1]
741
341
        if not branch_format_name:
742
342
            branch_format_name = None
743
343
        format = RemoteBranchFormat(network_name=branch_format_name)
744
344
        return RemoteBranch(self, self.find_repository(), format=format,
745
 
            setup_stacking=not ignore_fallbacks, name=name,
746
 
            possible_transports=possible_transports)
 
345
            setup_stacking=not ignore_fallbacks, name=name)
747
346
 
748
347
    def _open_repo_v1(self, path):
749
348
        verb = 'BzrDir.find_repository'
812
411
 
813
412
    def has_workingtree(self):
814
413
        if self._has_working_tree is None:
815
 
            path = self._path_for_remote_call(self._client)
816
 
            try:
817
 
                response = self._call('BzrDir.has_workingtree', path)
818
 
            except errors.UnknownSmartMethod:
819
 
                self._ensure_real()
820
 
                self._has_working_tree = self._real_bzrdir.has_workingtree()
821
 
            else:
822
 
                if response[0] not in ('yes', 'no'):
823
 
                    raise SmartProtocolError('unexpected response code %s' % (response,))
824
 
                self._has_working_tree = (response[0] == 'yes')
 
414
            self._ensure_real()
 
415
            self._has_working_tree = self._real_bzrdir.has_workingtree()
825
416
        return self._has_working_tree
826
417
 
827
418
    def open_workingtree(self, recommend_upgrade=True):
832
423
 
833
424
    def _path_for_remote_call(self, client):
834
425
        """Return the path to be used for this bzrdir in a remote call."""
835
 
        return urlutils.split_segment_parameters_raw(
836
 
            client.remote_path_from_transport(self.root_transport))[0]
 
426
        return client.remote_path_from_transport(self.root_transport)
837
427
 
838
428
    def get_branch_transport(self, branch_format, name=None):
839
429
        self._ensure_real()
851
441
        """Upgrading of remote bzrdirs is not supported yet."""
852
442
        return False
853
443
 
854
 
    def needs_format_conversion(self, format):
 
444
    def needs_format_conversion(self, format=None):
855
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)')
856
449
        return False
857
450
 
 
451
    def clone(self, url, revision_id=None, force_new_repo=False,
 
452
              preserve_stacking=False):
 
453
        self._ensure_real()
 
454
        return self._real_bzrdir.clone(url, revision_id=revision_id,
 
455
            force_new_repo=force_new_repo, preserve_stacking=preserve_stacking)
 
456
 
858
457
    def _get_config(self):
859
458
        return RemoteBzrDirConfig(self)
860
459
 
861
 
    def _get_config_store(self):
862
 
        return RemoteControlStore(self)
863
 
 
864
 
 
865
 
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
 
460
 
 
461
class RemoteRepositoryFormat(repository.RepositoryFormat):
866
462
    """Format for repositories accessed over a _SmartClient.
867
463
 
868
464
    Instances of this repository are represented by RemoteRepository
883
479
    """
884
480
 
885
481
    _matchingbzrdir = RemoteBzrDirFormat()
886
 
    supports_full_versioned_files = True
887
 
    supports_leaving_lock = True
888
482
 
889
483
    def __init__(self):
890
 
        _mod_repository.RepositoryFormat.__init__(self)
 
484
        repository.RepositoryFormat.__init__(self)
891
485
        self._custom_format = None
892
486
        self._network_name = None
893
487
        self._creating_bzrdir = None
894
 
        self._revision_graph_can_have_wrong_parents = None
895
488
        self._supports_chks = None
896
489
        self._supports_external_lookups = None
897
490
        self._supports_tree_reference = None
898
 
        self._supports_funky_characters = None
899
 
        self._supports_nesting_repositories = None
900
491
        self._rich_root_data = None
901
492
 
902
493
    def __repr__(self):
931
522
        return self._supports_external_lookups
932
523
 
933
524
    @property
934
 
    def supports_funky_characters(self):
935
 
        if self._supports_funky_characters is None:
936
 
            self._ensure_real()
937
 
            self._supports_funky_characters = \
938
 
                self._custom_format.supports_funky_characters
939
 
        return self._supports_funky_characters
940
 
 
941
 
    @property
942
 
    def supports_nesting_repositories(self):
943
 
        if self._supports_nesting_repositories is None:
944
 
            self._ensure_real()
945
 
            self._supports_nesting_repositories = \
946
 
                self._custom_format.supports_nesting_repositories
947
 
        return self._supports_nesting_repositories
948
 
 
949
 
    @property
950
525
    def supports_tree_reference(self):
951
526
        if self._supports_tree_reference is None:
952
527
            self._ensure_real()
954
529
                self._custom_format.supports_tree_reference
955
530
        return self._supports_tree_reference
956
531
 
957
 
    @property
958
 
    def revision_graph_can_have_wrong_parents(self):
959
 
        if self._revision_graph_can_have_wrong_parents is None:
960
 
            self._ensure_real()
961
 
            self._revision_graph_can_have_wrong_parents = \
962
 
                self._custom_format.revision_graph_can_have_wrong_parents
963
 
        return self._revision_graph_can_have_wrong_parents
964
 
 
965
532
    def _vfs_initialize(self, a_bzrdir, shared):
966
533
        """Helper for common code in initialize."""
967
534
        if self._custom_format:
1002
569
            network_name = self._network_name
1003
570
        else:
1004
571
            # Select the current bzrlib default and ask for that.
1005
 
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
 
572
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
1006
573
            reference_format = reference_bzrdir_format.repository_format
1007
574
            network_name = reference_format.network_name()
1008
575
        # 2) try direct creation via RPC
1034
601
 
1035
602
    def _ensure_real(self):
1036
603
        if self._custom_format is None:
1037
 
            try:
1038
 
                self._custom_format = _mod_repository.network_format_registry.get(
1039
 
                    self._network_name)
1040
 
            except KeyError:
1041
 
                raise errors.UnknownFormatError(kind='repository',
1042
 
                    format=self._network_name)
 
604
            self._custom_format = repository.network_format_registry.get(
 
605
                self._network_name)
1043
606
 
1044
607
    @property
1045
608
    def _fetch_order(self):
1080
643
        return self._custom_format._serializer
1081
644
 
1082
645
 
1083
 
class RemoteRepository(_mod_repository.Repository, _RpcHelper,
1084
 
        lock._RelockDebugMixin):
 
646
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
 
647
    bzrdir.ControlComponent):
1085
648
    """Repository accessed over rpc.
1086
649
 
1087
650
    For the moment most operations are performed using local transport-backed
1111
674
        self._format = format
1112
675
        self._lock_mode = None
1113
676
        self._lock_token = None
1114
 
        self._write_group_tokens = None
1115
677
        self._lock_count = 0
1116
678
        self._leave_lock = False
1117
679
        # Cache of revision parents; misses are cached during read locks, and
1141
703
        # transport, but I'm not sure it's worth making this method
1142
704
        # optional -- mbp 2010-04-21
1143
705
        return self.bzrdir.get_repository_transport(None)
1144
 
 
 
706
        
1145
707
    def __str__(self):
1146
708
        return "%s(%s)" % (self.__class__.__name__, self.base)
1147
709
 
1157
719
 
1158
720
        :param suppress_errors: see Repository.abort_write_group.
1159
721
        """
1160
 
        if self._real_repository:
1161
 
            self._ensure_real()
1162
 
            return self._real_repository.abort_write_group(
1163
 
                suppress_errors=suppress_errors)
1164
 
        if not self.is_in_write_group():
1165
 
            if suppress_errors:
1166
 
                mutter('(suppressed) not in write group')
1167
 
                return
1168
 
            raise errors.BzrError("not in write group")
1169
 
        path = self.bzrdir._path_for_remote_call(self._client)
1170
 
        try:
1171
 
            response = self._call('Repository.abort_write_group', path,
1172
 
                self._lock_token, self._write_group_tokens)
1173
 
        except Exception, exc:
1174
 
            self._write_group = None
1175
 
            if not suppress_errors:
1176
 
                raise
1177
 
            mutter('abort_write_group failed')
1178
 
            log_exception_quietly()
1179
 
            note(gettext('bzr: ERROR (ignored): %s'), exc)
1180
 
        else:
1181
 
            if response != ('ok', ):
1182
 
                raise errors.UnexpectedSmartServerResponse(response)
1183
 
            self._write_group_tokens = None
 
722
        self._ensure_real()
 
723
        return self._real_repository.abort_write_group(
 
724
            suppress_errors=suppress_errors)
1184
725
 
1185
726
    @property
1186
727
    def chk_bytes(self):
1200
741
        for older plugins that don't use e.g. the CommitBuilder
1201
742
        facility.
1202
743
        """
1203
 
        if self._real_repository:
1204
 
            self._ensure_real()
1205
 
            return self._real_repository.commit_write_group()
1206
 
        if not self.is_in_write_group():
1207
 
            raise errors.BzrError("not in write group")
1208
 
        path = self.bzrdir._path_for_remote_call(self._client)
1209
 
        response = self._call('Repository.commit_write_group', path,
1210
 
            self._lock_token, self._write_group_tokens)
1211
 
        if response != ('ok', ):
1212
 
            raise errors.UnexpectedSmartServerResponse(response)
1213
 
        self._write_group_tokens = None
 
744
        self._ensure_real()
 
745
        return self._real_repository.commit_write_group()
1214
746
 
1215
747
    def resume_write_group(self, tokens):
1216
 
        if self._real_repository:
1217
 
            return self._real_repository.resume_write_group(tokens)
1218
 
        path = self.bzrdir._path_for_remote_call(self._client)
1219
 
        try:
1220
 
            response = self._call('Repository.check_write_group', path,
1221
 
               self._lock_token, tokens)
1222
 
        except errors.UnknownSmartMethod:
1223
 
            self._ensure_real()
1224
 
            return self._real_repository.resume_write_group(tokens)
1225
 
        if response != ('ok', ):
1226
 
            raise errors.UnexpectedSmartServerResponse(response)
1227
 
        self._write_group_tokens = tokens
 
748
        self._ensure_real()
 
749
        return self._real_repository.resume_write_group(tokens)
1228
750
 
1229
751
    def suspend_write_group(self):
1230
 
        if self._real_repository:
1231
 
            return self._real_repository.suspend_write_group()
1232
 
        ret = self._write_group_tokens or []
1233
 
        self._write_group_tokens = None
1234
 
        return ret
 
752
        self._ensure_real()
 
753
        return self._real_repository.suspend_write_group()
1235
754
 
1236
755
    def get_missing_parent_inventories(self, check_for_missing_texts=True):
1237
756
        self._ensure_real()
1298
817
    def find_text_key_references(self):
1299
818
        """Find the text key references within the repository.
1300
819
 
 
820
        :return: a dictionary mapping (file_id, revision_id) tuples to altered file-ids to an iterable of
 
821
        revision_ids. Each altered file-ids has the exact revision_ids that
 
822
        altered it listed explicitly.
1301
823
        :return: A dictionary mapping text keys ((fileid, revision_id) tuples)
1302
824
            to whether they were referred to by the inventory of the
1303
825
            revision_id that they contain. The inventory texts from all present
1321
843
        """Private method for using with old (< 1.2) servers to fallback."""
1322
844
        if revision_id is None:
1323
845
            revision_id = ''
1324
 
        elif _mod_revision.is_null(revision_id):
 
846
        elif revision.is_null(revision_id):
1325
847
            return {}
1326
848
 
1327
849
        path = self.bzrdir._path_for_remote_call(self._client)
1351
873
        return RemoteStreamSource(self, to_format)
1352
874
 
1353
875
    @needs_read_lock
1354
 
    def get_file_graph(self):
1355
 
        return graph.Graph(self.texts)
1356
 
 
1357
 
    @needs_read_lock
1358
876
    def has_revision(self, revision_id):
1359
877
        """True if this repository has a copy of the revision."""
1360
878
        # Copy of bzrlib.repository.Repository.has_revision
1377
895
    def _has_same_fallbacks(self, other_repo):
1378
896
        """Returns true if the repositories have the same fallbacks."""
1379
897
        # XXX: copied from Repository; it should be unified into a base class
1380
 
        # <https://bugs.launchpad.net/bzr/+bug/401622>
 
898
        # <https://bugs.edge.launchpad.net/bzr/+bug/401622>
1381
899
        my_fb = self._fallback_repositories
1382
900
        other_fb = other_repo._fallback_repositories
1383
901
        if len(my_fb) != len(other_fb):
1412
930
        """See Repository.gather_stats()."""
1413
931
        path = self.bzrdir._path_for_remote_call(self._client)
1414
932
        # revid can be None to indicate no revisions, not just NULL_REVISION
1415
 
        if revid is None or _mod_revision.is_null(revid):
 
933
        if revid is None or revision.is_null(revid):
1416
934
            fmt_revid = ''
1417
935
        else:
1418
936
            fmt_revid = revid
1447
965
 
1448
966
    def get_physical_lock_status(self):
1449
967
        """See Repository.get_physical_lock_status()."""
1450
 
        path = self.bzrdir._path_for_remote_call(self._client)
1451
 
        try:
1452
 
            response = self._call('Repository.get_physical_lock_status', path)
1453
 
        except errors.UnknownSmartMethod:
1454
 
            self._ensure_real()
1455
 
            return self._real_repository.get_physical_lock_status()
1456
 
        if response[0] not in ('yes', 'no'):
1457
 
            raise errors.UnexpectedSmartServerResponse(response)
1458
 
        return (response[0] == 'yes')
 
968
        # should be an API call to the server.
 
969
        self._ensure_real()
 
970
        return self._real_repository.get_physical_lock_status()
1459
971
 
1460
972
    def is_in_write_group(self):
1461
973
        """Return True if there is an open write group.
1462
974
 
1463
975
        write groups are only applicable locally for the smart server..
1464
976
        """
1465
 
        if self._write_group_tokens is not None:
1466
 
            return True
1467
977
        if self._real_repository:
1468
978
            return self._real_repository.is_in_write_group()
1469
979
 
1487
997
        pass
1488
998
 
1489
999
    def lock_read(self):
1490
 
        """Lock the repository for read operations.
1491
 
 
1492
 
        :return: A bzrlib.lock.LogicalLockResult.
1493
 
        """
1494
1000
        # wrong eventually - want a local lock cache context
1495
1001
        if not self._lock_mode:
1496
1002
            self._note_lock('r')
1503
1009
                repo.lock_read()
1504
1010
        else:
1505
1011
            self._lock_count += 1
1506
 
        return lock.LogicalLockResult(self.unlock)
1507
1012
 
1508
1013
    def _remote_lock_write(self, token):
1509
1014
        path = self.bzrdir._path_for_remote_call(self._client)
1549
1054
            raise errors.ReadOnlyError(self)
1550
1055
        else:
1551
1056
            self._lock_count += 1
1552
 
        return RepositoryWriteLockResult(self.unlock, self._lock_token or None)
 
1057
        return self._lock_token or None
1553
1058
 
1554
1059
    def leave_lock_in_place(self):
1555
1060
        if not self._lock_token:
1604
1109
            self._real_repository.lock_write(self._lock_token)
1605
1110
        elif self._lock_mode == 'r':
1606
1111
            self._real_repository.lock_read()
1607
 
        if self._write_group_tokens is not None:
1608
 
            # if we are already in a write group, resume it
1609
 
            self._real_repository.resume_write_group(self._write_group_tokens)
1610
 
            self._write_group_tokens = None
1611
1112
 
1612
1113
    def start_write_group(self):
1613
1114
        """Start a write group on the decorated repository.
1617
1118
        for older plugins that don't use e.g. the CommitBuilder
1618
1119
        facility.
1619
1120
        """
1620
 
        if self._real_repository:
1621
 
            self._ensure_real()
1622
 
            return self._real_repository.start_write_group()
1623
 
        if not self.is_write_locked():
1624
 
            raise errors.NotWriteLocked(self)
1625
 
        if self._write_group_tokens is not None:
1626
 
            raise errors.BzrError('already in a write group')
1627
 
        path = self.bzrdir._path_for_remote_call(self._client)
1628
 
        try:
1629
 
            response = self._call('Repository.start_write_group', path,
1630
 
                self._lock_token)
1631
 
        except (errors.UnknownSmartMethod, errors.UnsuspendableWriteGroup):
1632
 
            self._ensure_real()
1633
 
            return self._real_repository.start_write_group()
1634
 
        if response[0] != 'ok':
1635
 
            raise errors.UnexpectedSmartServerResponse(response)
1636
 
        self._write_group_tokens = response[1]
 
1121
        self._ensure_real()
 
1122
        return self._real_repository.start_write_group()
1637
1123
 
1638
1124
    def _unlock(self, token):
1639
1125
        path = self.bzrdir._path_for_remote_call(self._client)
1666
1152
            # This is just to let the _real_repository stay up to date.
1667
1153
            if self._real_repository is not None:
1668
1154
                self._real_repository.unlock()
1669
 
            elif self._write_group_tokens is not None:
1670
 
                self.abort_write_group()
1671
1155
        finally:
1672
1156
            # The rpc-level lock should be released even if there was a
1673
1157
            # problem releasing the vfs-based lock.
1685
1169
 
1686
1170
    def break_lock(self):
1687
1171
        # should hand off to the network
1688
 
        path = self.bzrdir._path_for_remote_call(self._client)
1689
 
        try:
1690
 
            response = self._call("Repository.break_lock", path)
1691
 
        except errors.UnknownSmartMethod:
1692
 
            self._ensure_real()
1693
 
            return self._real_repository.break_lock()
1694
 
        if response != ('ok',):
1695
 
            raise errors.UnexpectedSmartServerResponse(response)
 
1172
        self._ensure_real()
 
1173
        return self._real_repository.break_lock()
1696
1174
 
1697
1175
    def _get_tarball(self, compression):
1698
1176
        """Return a TemporaryFile containing a repository tarball.
1716
1194
            return t
1717
1195
        raise errors.UnexpectedSmartServerResponse(response)
1718
1196
 
1719
 
    @needs_read_lock
1720
1197
    def sprout(self, to_bzrdir, revision_id=None):
1721
 
        """Create a descendent repository for new development.
1722
 
 
1723
 
        Unlike clone, this does not copy the settings of the repository.
1724
 
        """
1725
 
        dest_repo = self._create_sprouting_repo(to_bzrdir, shared=False)
 
1198
        # TODO: Option to control what format is created?
 
1199
        self._ensure_real()
 
1200
        dest_repo = self._real_repository._format.initialize(to_bzrdir,
 
1201
                                                             shared=False)
1726
1202
        dest_repo.fetch(self, revision_id=revision_id)
1727
1203
        return dest_repo
1728
1204
 
1729
 
    def _create_sprouting_repo(self, a_bzrdir, shared):
1730
 
        if not isinstance(a_bzrdir._format, self.bzrdir._format.__class__):
1731
 
            # use target default format.
1732
 
            dest_repo = a_bzrdir.create_repository()
1733
 
        else:
1734
 
            # Most control formats need the repository to be specifically
1735
 
            # created, but on some old all-in-one formats it's not needed
1736
 
            try:
1737
 
                dest_repo = self._format.initialize(a_bzrdir, shared=shared)
1738
 
            except errors.UninitializableFormat:
1739
 
                dest_repo = a_bzrdir.open_repository()
1740
 
        return dest_repo
1741
 
 
1742
1205
    ### These methods are just thin shims to the VFS object for now.
1743
1206
 
1744
 
    @needs_read_lock
1745
1207
    def revision_tree(self, revision_id):
1746
 
        revision_id = _mod_revision.ensure_null(revision_id)
1747
 
        if revision_id == _mod_revision.NULL_REVISION:
1748
 
            return InventoryRevisionTree(self,
1749
 
                Inventory(root_id=None), _mod_revision.NULL_REVISION)
1750
 
        else:
1751
 
            return list(self.revision_trees([revision_id]))[0]
 
1208
        self._ensure_real()
 
1209
        return self._real_repository.revision_tree(revision_id)
1752
1210
 
1753
1211
    def get_serializer_format(self):
1754
 
        path = self.bzrdir._path_for_remote_call(self._client)
1755
 
        try:
1756
 
            response = self._call('VersionedFileRepository.get_serializer_format',
1757
 
                path)
1758
 
        except errors.UnknownSmartMethod:
1759
 
            self._ensure_real()
1760
 
            return self._real_repository.get_serializer_format()
1761
 
        if response[0] != 'ok':
1762
 
            raise errors.UnexpectedSmartServerResponse(response)
1763
 
        return response[1]
 
1212
        self._ensure_real()
 
1213
        return self._real_repository.get_serializer_format()
1764
1214
 
1765
1215
    def get_commit_builder(self, branch, parents, config, timestamp=None,
1766
1216
                           timezone=None, committer=None, revprops=None,
1767
 
                           revision_id=None, lossy=False):
 
1217
                           revision_id=None):
1768
1218
        # FIXME: It ought to be possible to call this without immediately
1769
1219
        # triggering _ensure_real.  For now it's the easiest thing to do.
1770
1220
        self._ensure_real()
1771
1221
        real_repo = self._real_repository
1772
1222
        builder = real_repo.get_commit_builder(branch, parents,
1773
1223
                config, timestamp=timestamp, timezone=timezone,
1774
 
                committer=committer, revprops=revprops,
1775
 
                revision_id=revision_id, lossy=lossy)
 
1224
                committer=committer, revprops=revprops, revision_id=revision_id)
1776
1225
        return builder
1777
1226
 
1778
1227
    def add_fallback_repository(self, repository):
1786
1235
        # We need to accumulate additional repositories here, to pass them in
1787
1236
        # on various RPC's.
1788
1237
        #
1789
 
        # Make the check before we lock: this raises an exception.
1790
 
        self._check_fallback_repository(repository)
1791
1238
        if self.is_locked():
1792
1239
            # We will call fallback.unlock() when we transition to the unlocked
1793
1240
            # state, so always add a lock here. If a caller passes us a locked
1794
1241
            # repository, they are responsible for unlocking it later.
1795
1242
            repository.lock_read()
 
1243
        self._check_fallback_repository(repository)
1796
1244
        self._fallback_repositories.append(repository)
1797
1245
        # If self._real_repository was parameterised already (e.g. because a
1798
1246
        # _real_branch had its get_stacked_on_url method called), then the
1831
1279
 
1832
1280
    @needs_read_lock
1833
1281
    def get_inventory(self, revision_id):
1834
 
        return list(self.iter_inventories([revision_id]))[0]
1835
 
 
1836
 
    def _iter_inventories_rpc(self, revision_ids, ordering):
1837
 
        if ordering is None:
1838
 
            ordering = 'unordered'
1839
 
        path = self.bzrdir._path_for_remote_call(self._client)
1840
 
        body = "\n".join(revision_ids)
1841
 
        response_tuple, response_handler = (
1842
 
            self._call_with_body_bytes_expecting_body(
1843
 
                "VersionedFileRepository.get_inventories",
1844
 
                (path, ordering), body))
1845
 
        if response_tuple[0] != "ok":
1846
 
            raise errors.UnexpectedSmartServerResponse(response_tuple)
1847
 
        deserializer = inventory_delta.InventoryDeltaDeserializer()
1848
 
        byte_stream = response_handler.read_streamed_body()
1849
 
        decoded = smart_repo._byte_stream_to_stream(byte_stream)
1850
 
        if decoded is None:
1851
 
            # no results whatsoever
1852
 
            return
1853
 
        src_format, stream = decoded
1854
 
        if src_format.network_name() != self._format.network_name():
1855
 
            raise AssertionError(
1856
 
                "Mismatched RemoteRepository and stream src %r, %r" % (
1857
 
                src_format.network_name(), self._format.network_name()))
1858
 
        # ignore the src format, it's not really relevant
1859
 
        prev_inv = Inventory(root_id=None,
1860
 
            revision_id=_mod_revision.NULL_REVISION)
1861
 
        # there should be just one substream, with inventory deltas
1862
 
        substream_kind, substream = stream.next()
1863
 
        if substream_kind != "inventory-deltas":
1864
 
            raise AssertionError(
1865
 
                 "Unexpected stream %r received" % substream_kind)
1866
 
        for record in substream:
1867
 
            (parent_id, new_id, versioned_root, tree_references, invdelta) = (
1868
 
                deserializer.parse_text_bytes(record.get_bytes_as("fulltext")))
1869
 
            if parent_id != prev_inv.revision_id:
1870
 
                raise AssertionError("invalid base %r != %r" % (parent_id,
1871
 
                    prev_inv.revision_id))
1872
 
            inv = prev_inv.create_by_apply_delta(invdelta, new_id)
1873
 
            yield inv, inv.revision_id
1874
 
            prev_inv = inv
1875
 
 
1876
 
    def _iter_inventories_vfs(self, revision_ids, ordering=None):
1877
1282
        self._ensure_real()
1878
 
        return self._real_repository._iter_inventories(revision_ids, ordering)
 
1283
        return self._real_repository.get_inventory(revision_id)
1879
1284
 
1880
1285
    def iter_inventories(self, revision_ids, ordering=None):
1881
 
        """Get many inventories by revision_ids.
1882
 
 
1883
 
        This will buffer some or all of the texts used in constructing the
1884
 
        inventories in memory, but will only parse a single inventory at a
1885
 
        time.
1886
 
 
1887
 
        :param revision_ids: The expected revision ids of the inventories.
1888
 
        :param ordering: optional ordering, e.g. 'topological'.  If not
1889
 
            specified, the order of revision_ids will be preserved (by
1890
 
            buffering if necessary).
1891
 
        :return: An iterator of inventories.
1892
 
        """
1893
 
        if ((None in revision_ids)
1894
 
            or (_mod_revision.NULL_REVISION in revision_ids)):
1895
 
            raise ValueError('cannot get null revision inventory')
1896
 
        for inv, revid in self._iter_inventories(revision_ids, ordering):
1897
 
            if inv is None:
1898
 
                raise errors.NoSuchRevision(self, revid)
1899
 
            yield inv
1900
 
 
1901
 
    def _iter_inventories(self, revision_ids, ordering=None):
1902
 
        if len(revision_ids) == 0:
1903
 
            return
1904
 
        missing = set(revision_ids)
1905
 
        if ordering is None:
1906
 
            order_as_requested = True
1907
 
            invs = {}
1908
 
            order = list(revision_ids)
1909
 
            order.reverse()
1910
 
            next_revid = order.pop()
1911
 
        else:
1912
 
            order_as_requested = False
1913
 
            if ordering != 'unordered' and self._fallback_repositories:
1914
 
                raise ValueError('unsupported ordering %r' % ordering)
1915
 
        iter_inv_fns = [self._iter_inventories_rpc] + [
1916
 
            fallback._iter_inventories for fallback in
1917
 
            self._fallback_repositories]
1918
 
        try:
1919
 
            for iter_inv in iter_inv_fns:
1920
 
                request = [revid for revid in revision_ids if revid in missing]
1921
 
                for inv, revid in iter_inv(request, ordering):
1922
 
                    if inv is None:
1923
 
                        continue
1924
 
                    missing.remove(inv.revision_id)
1925
 
                    if ordering != 'unordered':
1926
 
                        invs[revid] = inv
1927
 
                    else:
1928
 
                        yield inv, revid
1929
 
                if order_as_requested:
1930
 
                    # Yield as many results as we can while preserving order.
1931
 
                    while next_revid in invs:
1932
 
                        inv = invs.pop(next_revid)
1933
 
                        yield inv, inv.revision_id
1934
 
                        try:
1935
 
                            next_revid = order.pop()
1936
 
                        except IndexError:
1937
 
                            # We still want to fully consume the stream, just
1938
 
                            # in case it is not actually finished at this point
1939
 
                            next_revid = None
1940
 
                            break
1941
 
        except errors.UnknownSmartMethod:
1942
 
            for inv, revid in self._iter_inventories_vfs(revision_ids, ordering):
1943
 
                yield inv, revid
1944
 
            return
1945
 
        # Report missing
1946
 
        if order_as_requested:
1947
 
            if next_revid is not None:
1948
 
                yield None, next_revid
1949
 
            while order:
1950
 
                revid = order.pop()
1951
 
                yield invs.get(revid), revid
1952
 
        else:
1953
 
            while missing:
1954
 
                yield None, missing.pop()
 
1286
        self._ensure_real()
 
1287
        return self._real_repository.iter_inventories(revision_ids, ordering)
1955
1288
 
1956
1289
    @needs_read_lock
1957
1290
    def get_revision(self, revision_id):
1958
 
        return self.get_revisions([revision_id])[0]
 
1291
        self._ensure_real()
 
1292
        return self._real_repository.get_revision(revision_id)
1959
1293
 
1960
1294
    def get_transaction(self):
1961
1295
        self._ensure_real()
1963
1297
 
1964
1298
    @needs_read_lock
1965
1299
    def clone(self, a_bzrdir, revision_id=None):
1966
 
        dest_repo = self._create_sprouting_repo(
1967
 
            a_bzrdir, shared=self.is_shared())
1968
 
        self.copy_content_into(dest_repo, revision_id)
1969
 
        return dest_repo
 
1300
        self._ensure_real()
 
1301
        return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
1970
1302
 
1971
1303
    def make_working_trees(self):
1972
1304
        """See Repository.make_working_trees"""
1973
 
        path = self.bzrdir._path_for_remote_call(self._client)
1974
 
        try:
1975
 
            response = self._call('Repository.make_working_trees', path)
1976
 
        except errors.UnknownSmartMethod:
1977
 
            self._ensure_real()
1978
 
            return self._real_repository.make_working_trees()
1979
 
        if response[0] not in ('yes', 'no'):
1980
 
            raise SmartProtocolError('unexpected response code %s' % (response,))
1981
 
        return response[0] == 'yes'
 
1305
        self._ensure_real()
 
1306
        return self._real_repository.make_working_trees()
1982
1307
 
1983
1308
    def refresh_data(self):
1984
 
        """Re-read any data needed to synchronise with disk.
 
1309
        """Re-read any data needed to to synchronise with disk.
1985
1310
 
1986
1311
        This method is intended to be called after another repository instance
1987
1312
        (such as one used by a smart server) has inserted data into the
1988
 
        repository. On all repositories this will work outside of write groups.
1989
 
        Some repository formats (pack and newer for bzrlib native formats)
1990
 
        support refresh_data inside write groups. If called inside a write
1991
 
        group on a repository that does not support refreshing in a write group
1992
 
        IsInWriteGroupError will be raised.
 
1313
        repository. It may not be called during a write group, but may be
 
1314
        called at any other time.
1993
1315
        """
 
1316
        if self.is_in_write_group():
 
1317
            raise errors.InternalBzrError(
 
1318
                "May not refresh_data while in a write group.")
1994
1319
        if self._real_repository is not None:
1995
1320
            self._real_repository.refresh_data()
1996
1321
 
2003
1328
        included_keys = result_set.intersection(result_parents)
2004
1329
        start_keys = result_set.difference(included_keys)
2005
1330
        exclude_keys = result_parents.difference(result_set)
2006
 
        result = vf_search.SearchResult(start_keys, exclude_keys,
 
1331
        result = graph.SearchResult(start_keys, exclude_keys,
2007
1332
            len(result_set), result_set)
2008
1333
        return result
2009
1334
 
2010
1335
    @needs_read_lock
2011
 
    def search_missing_revision_ids(self, other,
2012
 
            revision_id=symbol_versioning.DEPRECATED_PARAMETER,
2013
 
            find_ghosts=True, revision_ids=None, if_present_ids=None,
2014
 
            limit=None):
 
1336
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2015
1337
        """Return the revision ids that other has that this does not.
2016
1338
 
2017
1339
        These are returned in topological order.
2018
1340
 
2019
1341
        revision_id: only return revision ids included by revision_id.
2020
1342
        """
2021
 
        if symbol_versioning.deprecated_passed(revision_id):
2022
 
            symbol_versioning.warn(
2023
 
                'search_missing_revision_ids(revision_id=...) was '
2024
 
                'deprecated in 2.4.  Use revision_ids=[...] instead.',
2025
 
                DeprecationWarning, stacklevel=2)
2026
 
            if revision_ids is not None:
2027
 
                raise AssertionError(
2028
 
                    'revision_ids is mutually exclusive with revision_id')
2029
 
            if revision_id is not None:
2030
 
                revision_ids = [revision_id]
2031
 
        inter_repo = _mod_repository.InterRepository.get(other, self)
2032
 
        return inter_repo.search_missing_revision_ids(
2033
 
            find_ghosts=find_ghosts, revision_ids=revision_ids,
2034
 
            if_present_ids=if_present_ids, limit=limit)
 
1343
        return repository.InterRepository.get(
 
1344
            other, self).search_missing_revision_ids(revision_id, find_ghosts)
2035
1345
 
2036
 
    def fetch(self, source, revision_id=None, find_ghosts=False,
 
1346
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2037
1347
            fetch_spec=None):
2038
1348
        # No base implementation to use as RemoteRepository is not a subclass
2039
1349
        # of Repository; so this is a copy of Repository.fetch().
2050
1360
            # check that last_revision is in 'from' and then return a
2051
1361
            # no-operation.
2052
1362
            if (revision_id is not None and
2053
 
                not _mod_revision.is_null(revision_id)):
 
1363
                not revision.is_null(revision_id)):
2054
1364
                self.get_revision(revision_id)
2055
1365
            return 0, []
2056
1366
        # if there is no specific appropriate InterRepository, this will get
2057
1367
        # the InterRepository base class, which raises an
2058
1368
        # IncompatibleRepositories when asked to fetch.
2059
 
        inter = _mod_repository.InterRepository.get(source, self)
2060
 
        if (fetch_spec is not None and
2061
 
            not getattr(inter, "supports_fetch_spec", False)):
2062
 
            raise errors.UnsupportedOperation(
2063
 
                "fetch_spec not supported for %r" % inter)
2064
 
        return inter.fetch(revision_id=revision_id,
 
1369
        inter = repository.InterRepository.get(source, self)
 
1370
        return inter.fetch(revision_id=revision_id, pb=pb,
2065
1371
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
2066
1372
 
2067
1373
    def create_bundle(self, target, base, fileobj, format=None):
2069
1375
        self._real_repository.create_bundle(target, base, fileobj, format)
2070
1376
 
2071
1377
    @needs_read_lock
2072
 
    @symbol_versioning.deprecated_method(
2073
 
        symbol_versioning.deprecated_in((2, 4, 0)))
2074
1378
    def get_ancestry(self, revision_id, topo_sorted=True):
2075
1379
        self._ensure_real()
2076
1380
        return self._real_repository.get_ancestry(revision_id, topo_sorted)
2084
1388
        return self._real_repository._get_versioned_file_checker(
2085
1389
            revisions, revision_versions_cache)
2086
1390
 
2087
 
    def _iter_files_bytes_rpc(self, desired_files, absent):
2088
 
        path = self.bzrdir._path_for_remote_call(self._client)
2089
 
        lines = []
2090
 
        identifiers = []
2091
 
        for (file_id, revid, identifier) in desired_files:
2092
 
            lines.append("%s\0%s" % (
2093
 
                osutils.safe_file_id(file_id),
2094
 
                osutils.safe_revision_id(revid)))
2095
 
            identifiers.append(identifier)
2096
 
        (response_tuple, response_handler) = (
2097
 
            self._call_with_body_bytes_expecting_body(
2098
 
            "Repository.iter_files_bytes", (path, ), "\n".join(lines)))
2099
 
        if response_tuple != ('ok', ):
2100
 
            response_handler.cancel_read_body()
2101
 
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2102
 
        byte_stream = response_handler.read_streamed_body()
2103
 
        def decompress_stream(start, byte_stream, unused):
2104
 
            decompressor = zlib.decompressobj()
2105
 
            yield decompressor.decompress(start)
2106
 
            while decompressor.unused_data == "":
2107
 
                try:
2108
 
                    data = byte_stream.next()
2109
 
                except StopIteration:
2110
 
                    break
2111
 
                yield decompressor.decompress(data)
2112
 
            yield decompressor.flush()
2113
 
            unused.append(decompressor.unused_data)
2114
 
        unused = ""
2115
 
        while True:
2116
 
            while not "\n" in unused:
2117
 
                unused += byte_stream.next()
2118
 
            header, rest = unused.split("\n", 1)
2119
 
            args = header.split("\0")
2120
 
            if args[0] == "absent":
2121
 
                absent[identifiers[int(args[3])]] = (args[1], args[2])
2122
 
                unused = rest
2123
 
                continue
2124
 
            elif args[0] == "ok":
2125
 
                idx = int(args[1])
2126
 
            else:
2127
 
                raise errors.UnexpectedSmartServerResponse(args)
2128
 
            unused_chunks = []
2129
 
            yield (identifiers[idx],
2130
 
                decompress_stream(rest, byte_stream, unused_chunks))
2131
 
            unused = "".join(unused_chunks)
2132
 
 
2133
1391
    def iter_files_bytes(self, desired_files):
2134
1392
        """See Repository.iter_file_bytes.
2135
1393
        """
2136
 
        try:
2137
 
            absent = {}
2138
 
            for (identifier, bytes_iterator) in self._iter_files_bytes_rpc(
2139
 
                    desired_files, absent):
2140
 
                yield identifier, bytes_iterator
2141
 
            for fallback in self._fallback_repositories:
2142
 
                if not absent:
2143
 
                    break
2144
 
                desired_files = [(key[0], key[1], identifier) for
2145
 
                    (identifier, key) in absent.iteritems()]
2146
 
                for (identifier, bytes_iterator) in fallback.iter_files_bytes(desired_files):
2147
 
                    del absent[identifier]
2148
 
                    yield identifier, bytes_iterator
2149
 
            if absent:
2150
 
                # There may be more missing items, but raise an exception
2151
 
                # for just one.
2152
 
                missing_identifier = absent.keys()[0]
2153
 
                missing_key = absent[missing_identifier]
2154
 
                raise errors.RevisionNotPresent(revision_id=missing_key[1],
2155
 
                    file_id=missing_key[0])
2156
 
        except errors.UnknownSmartMethod:
2157
 
            self._ensure_real()
2158
 
            for (identifier, bytes_iterator) in (
2159
 
                self._real_repository.iter_files_bytes(desired_files)):
2160
 
                yield identifier, bytes_iterator
2161
 
 
2162
 
    def get_cached_parent_map(self, revision_ids):
2163
 
        """See bzrlib.CachingParentsProvider.get_cached_parent_map"""
2164
 
        return self._unstacked_provider.get_cached_parent_map(revision_ids)
 
1394
        self._ensure_real()
 
1395
        return self._real_repository.iter_files_bytes(desired_files)
2165
1396
 
2166
1397
    def get_parent_map(self, revision_ids):
2167
1398
        """See bzrlib.Graph.get_parent_map()."""
2226
1457
        if parents_map is None:
2227
1458
            # Repository is not locked, so there's no cache.
2228
1459
            parents_map = {}
2229
 
        if _DEFAULT_SEARCH_DEPTH <= 0:
2230
 
            (start_set, stop_keys,
2231
 
             key_count) = vf_search.search_result_from_parent_map(
2232
 
                parents_map, self._unstacked_provider.missing_keys)
2233
 
        else:
2234
 
            (start_set, stop_keys,
2235
 
             key_count) = vf_search.limited_search_result_from_parent_map(
2236
 
                parents_map, self._unstacked_provider.missing_keys,
2237
 
                keys, depth=_DEFAULT_SEARCH_DEPTH)
 
1460
        # start_set is all the keys in the cache
 
1461
        start_set = set(parents_map)
 
1462
        # result set is all the references to keys in the cache
 
1463
        result_parents = set()
 
1464
        for parents in parents_map.itervalues():
 
1465
            result_parents.update(parents)
 
1466
        stop_keys = result_parents.difference(start_set)
 
1467
        # We don't need to send ghosts back to the server as a position to
 
1468
        # stop either.
 
1469
        stop_keys.difference_update(self._unstacked_provider.missing_keys)
 
1470
        key_count = len(parents_map)
 
1471
        if (NULL_REVISION in result_parents
 
1472
            and NULL_REVISION in self._unstacked_provider.missing_keys):
 
1473
            # If we pruned NULL_REVISION from the stop_keys because it's also
 
1474
            # in our cache of "missing" keys we need to increment our key count
 
1475
            # by 1, because the reconsitituted SearchResult on the server will
 
1476
            # still consider NULL_REVISION to be an included key.
 
1477
            key_count += 1
 
1478
        included_keys = start_set.intersection(result_parents)
 
1479
        start_set.difference_update(included_keys)
2238
1480
        recipe = ('manual', start_set, stop_keys, key_count)
2239
1481
        body = self._serialise_search_recipe(recipe)
2240
1482
        path = self.bzrdir._path_for_remote_call(self._client)
2289
1531
 
2290
1532
    @needs_read_lock
2291
1533
    def get_signature_text(self, revision_id):
2292
 
        path = self.bzrdir._path_for_remote_call(self._client)
2293
 
        try:
2294
 
            response_tuple, response_handler = self._call_expecting_body(
2295
 
                'Repository.get_revision_signature_text', path, revision_id)
2296
 
        except errors.UnknownSmartMethod:
2297
 
            self._ensure_real()
2298
 
            return self._real_repository.get_signature_text(revision_id)
2299
 
        except errors.NoSuchRevision, err:
2300
 
            for fallback in self._fallback_repositories:
2301
 
                try:
2302
 
                    return fallback.get_signature_text(revision_id)
2303
 
                except errors.NoSuchRevision:
2304
 
                    pass
2305
 
            raise err
2306
 
        else:
2307
 
            if response_tuple[0] != 'ok':
2308
 
                raise errors.UnexpectedSmartServerResponse(response_tuple)
2309
 
            return response_handler.read_body_bytes()
 
1534
        self._ensure_real()
 
1535
        return self._real_repository.get_signature_text(revision_id)
2310
1536
 
2311
1537
    @needs_read_lock
2312
1538
    def _get_inventory_xml(self, revision_id):
2313
 
        # This call is used by older working tree formats,
2314
 
        # which stored a serialized basis inventory.
2315
1539
        self._ensure_real()
2316
1540
        return self._real_repository._get_inventory_xml(revision_id)
2317
1541
 
2318
 
    @needs_write_lock
2319
1542
    def reconcile(self, other=None, thorough=False):
2320
 
        from bzrlib.reconcile import RepoReconciler
2321
 
        path = self.bzrdir._path_for_remote_call(self._client)
2322
 
        try:
2323
 
            response, handler = self._call_expecting_body(
2324
 
                'Repository.reconcile', path, self._lock_token)
2325
 
        except (errors.UnknownSmartMethod, errors.TokenLockingNotSupported):
2326
 
            self._ensure_real()
2327
 
            return self._real_repository.reconcile(other=other, thorough=thorough)
2328
 
        if response != ('ok', ):
2329
 
            raise errors.UnexpectedSmartServerResponse(response)
2330
 
        body = handler.read_body_bytes()
2331
 
        result = RepoReconciler(self)
2332
 
        for line in body.split('\n'):
2333
 
            if not line:
2334
 
                continue
2335
 
            key, val_text = line.split(':')
2336
 
            if key == "garbage_inventories":
2337
 
                result.garbage_inventories = int(val_text)
2338
 
            elif key == "inconsistent_parents":
2339
 
                result.inconsistent_parents = int(val_text)
2340
 
            else:
2341
 
                mutter("unknown reconcile key %r" % key)
2342
 
        return result
 
1543
        self._ensure_real()
 
1544
        return self._real_repository.reconcile(other=other, thorough=thorough)
2343
1545
 
2344
1546
    def all_revision_ids(self):
2345
 
        path = self.bzrdir._path_for_remote_call(self._client)
2346
 
        try:
2347
 
            response_tuple, response_handler = self._call_expecting_body(
2348
 
                "Repository.all_revision_ids", path)
2349
 
        except errors.UnknownSmartMethod:
2350
 
            self._ensure_real()
2351
 
            return self._real_repository.all_revision_ids()
2352
 
        if response_tuple != ("ok", ):
2353
 
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2354
 
        revids = set(response_handler.read_body_bytes().splitlines())
2355
 
        for fallback in self._fallback_repositories:
2356
 
            revids.update(set(fallback.all_revision_ids()))
2357
 
        return list(revids)
2358
 
 
2359
 
    def _filtered_revision_trees(self, revision_ids, file_ids):
2360
 
        """Return Tree for a revision on this branch with only some files.
2361
 
 
2362
 
        :param revision_ids: a sequence of revision-ids;
2363
 
          a revision-id may not be None or 'null:'
2364
 
        :param file_ids: if not None, the result is filtered
2365
 
          so that only those file-ids, their parents and their
2366
 
          children are included.
2367
 
        """
2368
 
        inventories = self.iter_inventories(revision_ids)
2369
 
        for inv in inventories:
2370
 
            # Should we introduce a FilteredRevisionTree class rather
2371
 
            # than pre-filter the inventory here?
2372
 
            filtered_inv = inv.filter(file_ids)
2373
 
            yield InventoryRevisionTree(self, filtered_inv, filtered_inv.revision_id)
 
1547
        self._ensure_real()
 
1548
        return self._real_repository.all_revision_ids()
2374
1549
 
2375
1550
    @needs_read_lock
2376
1551
    def get_deltas_for_revisions(self, revisions, specific_fileids=None):
2377
 
        medium = self._client._medium
2378
 
        if medium._is_remote_before((1, 2)):
2379
 
            self._ensure_real()
2380
 
            for delta in self._real_repository.get_deltas_for_revisions(
2381
 
                    revisions, specific_fileids):
2382
 
                yield delta
2383
 
            return
2384
 
        # Get the revision-ids of interest
2385
 
        required_trees = set()
2386
 
        for revision in revisions:
2387
 
            required_trees.add(revision.revision_id)
2388
 
            required_trees.update(revision.parent_ids[:1])
2389
 
 
2390
 
        # Get the matching filtered trees. Note that it's more
2391
 
        # efficient to pass filtered trees to changes_from() rather
2392
 
        # than doing the filtering afterwards. changes_from() could
2393
 
        # arguably do the filtering itself but it's path-based, not
2394
 
        # file-id based, so filtering before or afterwards is
2395
 
        # currently easier.
2396
 
        if specific_fileids is None:
2397
 
            trees = dict((t.get_revision_id(), t) for
2398
 
                t in self.revision_trees(required_trees))
2399
 
        else:
2400
 
            trees = dict((t.get_revision_id(), t) for
2401
 
                t in self._filtered_revision_trees(required_trees,
2402
 
                specific_fileids))
2403
 
 
2404
 
        # Calculate the deltas
2405
 
        for revision in revisions:
2406
 
            if not revision.parent_ids:
2407
 
                old_tree = self.revision_tree(_mod_revision.NULL_REVISION)
2408
 
            else:
2409
 
                old_tree = trees[revision.parent_ids[0]]
2410
 
            yield trees[revision.revision_id].changes_from(old_tree)
 
1552
        self._ensure_real()
 
1553
        return self._real_repository.get_deltas_for_revisions(revisions,
 
1554
            specific_fileids=specific_fileids)
2411
1555
 
2412
1556
    @needs_read_lock
2413
1557
    def get_revision_delta(self, revision_id, specific_fileids=None):
2414
 
        r = self.get_revision(revision_id)
2415
 
        return list(self.get_deltas_for_revisions([r],
2416
 
            specific_fileids=specific_fileids))[0]
 
1558
        self._ensure_real()
 
1559
        return self._real_repository.get_revision_delta(revision_id,
 
1560
            specific_fileids=specific_fileids)
2417
1561
 
2418
1562
    @needs_read_lock
2419
1563
    def revision_trees(self, revision_ids):
2420
 
        inventories = self.iter_inventories(revision_ids)
2421
 
        for inv in inventories:
2422
 
            yield InventoryRevisionTree(self, inv, inv.revision_id)
 
1564
        self._ensure_real()
 
1565
        return self._real_repository.revision_trees(revision_ids)
2423
1566
 
2424
1567
    @needs_read_lock
2425
1568
    def get_revision_reconcile(self, revision_id):
2433
1576
            callback_refs=callback_refs, check_repo=check_repo)
2434
1577
 
2435
1578
    def copy_content_into(self, destination, revision_id=None):
2436
 
        """Make a complete copy of the content in self into destination.
2437
 
 
2438
 
        This is a destructive operation! Do not use it on existing
2439
 
        repositories.
2440
 
        """
2441
 
        interrepo = _mod_repository.InterRepository.get(self, destination)
2442
 
        return interrepo.copy_content(revision_id)
 
1579
        self._ensure_real()
 
1580
        return self._real_repository.copy_content_into(
 
1581
            destination, revision_id=revision_id)
2443
1582
 
2444
1583
    def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
2445
1584
        # get a tarball of the remote repository, and copy from that into the
2446
1585
        # destination
 
1586
        from bzrlib import osutils
2447
1587
        import tarfile
2448
1588
        # TODO: Maybe a progress bar while streaming the tarball?
2449
 
        note(gettext("Copying repository content as tarball..."))
 
1589
        note("Copying repository content as tarball...")
2450
1590
        tar_file = self._get_tarball('bz2')
2451
1591
        if tar_file is None:
2452
1592
            return None
2457
1597
            tmpdir = osutils.mkdtemp()
2458
1598
            try:
2459
1599
                _extract_tar(tar, tmpdir)
2460
 
                tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
 
1600
                tmp_bzrdir = BzrDir.open(tmpdir)
2461
1601
                tmp_repo = tmp_bzrdir.open_repository()
2462
1602
                tmp_repo.copy_content_into(destination, revision_id)
2463
1603
            finally:
2481
1621
    @needs_write_lock
2482
1622
    def pack(self, hint=None, clean_obsolete_packs=False):
2483
1623
        """Compress the data within the repository.
 
1624
 
 
1625
        This is not currently implemented within the smart server.
2484
1626
        """
2485
 
        if hint is None:
2486
 
            body = ""
2487
 
        else:
2488
 
            body = "".join([l+"\n" for l in hint])
2489
 
        path = self.bzrdir._path_for_remote_call(self._client)
2490
 
        try:
2491
 
            response, handler = self._call_with_body_bytes_expecting_body(
2492
 
                'Repository.pack', (path, self._lock_token,
2493
 
                    str(clean_obsolete_packs)), body)
2494
 
        except errors.UnknownSmartMethod:
2495
 
            self._ensure_real()
2496
 
            return self._real_repository.pack(hint=hint,
2497
 
                clean_obsolete_packs=clean_obsolete_packs)
2498
 
        handler.cancel_read_body()
2499
 
        if response != ('ok', ):
2500
 
            raise errors.UnexpectedSmartServerResponse(response)
 
1627
        self._ensure_real()
 
1628
        return self._real_repository.pack(hint=hint, clean_obsolete_packs=clean_obsolete_packs)
2501
1629
 
2502
1630
    @property
2503
1631
    def revisions(self):
2504
1632
        """Decorate the real repository for now.
2505
1633
 
 
1634
        In the short term this should become a real object to intercept graph
 
1635
        lookups.
 
1636
 
2506
1637
        In the long term a full blown network facility is needed.
2507
1638
        """
2508
1639
        self._ensure_real()
2536
1667
 
2537
1668
    @needs_write_lock
2538
1669
    def sign_revision(self, revision_id, gpg_strategy):
2539
 
        testament = _mod_testament.Testament.from_revision(self, revision_id)
2540
 
        plaintext = testament.as_short_text()
2541
 
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
 
1670
        self._ensure_real()
 
1671
        return self._real_repository.sign_revision(revision_id, gpg_strategy)
2542
1672
 
2543
1673
    @property
2544
1674
    def texts(self):
2550
1680
        self._ensure_real()
2551
1681
        return self._real_repository.texts
2552
1682
 
2553
 
    def _iter_revisions_rpc(self, revision_ids):
2554
 
        body = "\n".join(revision_ids)
2555
 
        path = self.bzrdir._path_for_remote_call(self._client)
2556
 
        response_tuple, response_handler = (
2557
 
            self._call_with_body_bytes_expecting_body(
2558
 
            "Repository.iter_revisions", (path, ), body))
2559
 
        if response_tuple[0] != "ok":
2560
 
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2561
 
        serializer_format = response_tuple[1]
2562
 
        serializer = serializer_format_registry.get(serializer_format)
2563
 
        byte_stream = response_handler.read_streamed_body()
2564
 
        decompressor = zlib.decompressobj()
2565
 
        chunks = []
2566
 
        for bytes in byte_stream:
2567
 
            chunks.append(decompressor.decompress(bytes))
2568
 
            if decompressor.unused_data != "":
2569
 
                chunks.append(decompressor.flush())
2570
 
                yield serializer.read_revision_from_string("".join(chunks))
2571
 
                unused = decompressor.unused_data
2572
 
                decompressor = zlib.decompressobj()
2573
 
                chunks = [decompressor.decompress(unused)]
2574
 
        chunks.append(decompressor.flush())
2575
 
        text = "".join(chunks)
2576
 
        if text != "":
2577
 
            yield serializer.read_revision_from_string("".join(chunks))
2578
 
 
2579
1683
    @needs_read_lock
2580
1684
    def get_revisions(self, revision_ids):
2581
 
        if revision_ids is None:
2582
 
            revision_ids = self.all_revision_ids()
2583
 
        else:
2584
 
            for rev_id in revision_ids:
2585
 
                if not rev_id or not isinstance(rev_id, basestring):
2586
 
                    raise errors.InvalidRevisionId(
2587
 
                        revision_id=rev_id, branch=self)
2588
 
        try:
2589
 
            missing = set(revision_ids)
2590
 
            revs = {}
2591
 
            for rev in self._iter_revisions_rpc(revision_ids):
2592
 
                missing.remove(rev.revision_id)
2593
 
                revs[rev.revision_id] = rev
2594
 
        except errors.UnknownSmartMethod:
2595
 
            self._ensure_real()
2596
 
            return self._real_repository.get_revisions(revision_ids)
2597
 
        for fallback in self._fallback_repositories:
2598
 
            if not missing:
2599
 
                break
2600
 
            for revid in list(missing):
2601
 
                # XXX JRV 2011-11-20: It would be nice if there was a
2602
 
                # public method on Repository that could be used to query
2603
 
                # for revision objects *without* failing completely if one
2604
 
                # was missing. There is VersionedFileRepository._iter_revisions,
2605
 
                # but unfortunately that's private and not provided by
2606
 
                # all repository implementations.
2607
 
                try:
2608
 
                    revs[revid] = fallback.get_revision(revid)
2609
 
                except errors.NoSuchRevision:
2610
 
                    pass
2611
 
                else:
2612
 
                    missing.remove(revid)
2613
 
        if missing:
2614
 
            raise errors.NoSuchRevision(self, list(missing)[0])
2615
 
        return [revs[revid] for revid in revision_ids]
 
1685
        self._ensure_real()
 
1686
        return self._real_repository.get_revisions(revision_ids)
2616
1687
 
2617
1688
    def supports_rich_root(self):
2618
1689
        return self._format.rich_root_data
2619
1690
 
2620
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2621
1691
    def iter_reverse_revision_history(self, revision_id):
2622
1692
        self._ensure_real()
2623
1693
        return self._real_repository.iter_reverse_revision_history(revision_id)
2626
1696
    def _serializer(self):
2627
1697
        return self._format._serializer
2628
1698
 
2629
 
    @needs_write_lock
2630
1699
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
2631
 
        signature = gpg_strategy.sign(plaintext)
2632
 
        self.add_signature_text(revision_id, signature)
 
1700
        self._ensure_real()
 
1701
        return self._real_repository.store_revision_signature(
 
1702
            gpg_strategy, plaintext, revision_id)
2633
1703
 
2634
1704
    def add_signature_text(self, revision_id, signature):
2635
 
        if self._real_repository:
2636
 
            # If there is a real repository the write group will
2637
 
            # be in the real repository as well, so use that:
2638
 
            self._ensure_real()
2639
 
            return self._real_repository.add_signature_text(
2640
 
                revision_id, signature)
2641
 
        path = self.bzrdir._path_for_remote_call(self._client)
2642
 
        response, handler = self._call_with_body_bytes_expecting_body(
2643
 
            'Repository.add_signature_text', (path, self._lock_token,
2644
 
                revision_id) + tuple(self._write_group_tokens), signature)
2645
 
        handler.cancel_read_body()
2646
 
        self.refresh_data()
2647
 
        if response[0] != 'ok':
2648
 
            raise errors.UnexpectedSmartServerResponse(response)
2649
 
        self._write_group_tokens = response[1:]
 
1705
        self._ensure_real()
 
1706
        return self._real_repository.add_signature_text(revision_id, signature)
2650
1707
 
2651
1708
    def has_signature_for_revision_id(self, revision_id):
2652
 
        path = self.bzrdir._path_for_remote_call(self._client)
2653
 
        try:
2654
 
            response = self._call('Repository.has_signature_for_revision_id',
2655
 
                path, revision_id)
2656
 
        except errors.UnknownSmartMethod:
2657
 
            self._ensure_real()
2658
 
            return self._real_repository.has_signature_for_revision_id(
2659
 
                revision_id)
2660
 
        if response[0] not in ('yes', 'no'):
2661
 
            raise SmartProtocolError('unexpected response code %s' % (response,))
2662
 
        if response[0] == 'yes':
2663
 
            return True
2664
 
        for fallback in self._fallback_repositories:
2665
 
            if fallback.has_signature_for_revision_id(revision_id):
2666
 
                return True
2667
 
        return False
2668
 
 
2669
 
    @needs_read_lock
2670
 
    def verify_revision_signature(self, revision_id, gpg_strategy):
2671
 
        if not self.has_signature_for_revision_id(revision_id):
2672
 
            return gpg.SIGNATURE_NOT_SIGNED, None
2673
 
        signature = self.get_signature_text(revision_id)
2674
 
 
2675
 
        testament = _mod_testament.Testament.from_revision(self, revision_id)
2676
 
        plaintext = testament.as_short_text()
2677
 
 
2678
 
        return gpg_strategy.verify(signature, plaintext)
 
1709
        self._ensure_real()
 
1710
        return self._real_repository.has_signature_for_revision_id(revision_id)
2679
1711
 
2680
1712
    def item_keys_introduced_by(self, revision_ids, _files_pb=None):
2681
1713
        self._ensure_real()
2682
1714
        return self._real_repository.item_keys_introduced_by(revision_ids,
2683
1715
            _files_pb=_files_pb)
2684
1716
 
 
1717
    def revision_graph_can_have_wrong_parents(self):
 
1718
        # The answer depends on the remote repo format.
 
1719
        self._ensure_real()
 
1720
        return self._real_repository.revision_graph_can_have_wrong_parents()
 
1721
 
2685
1722
    def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2686
1723
        self._ensure_real()
2687
1724
        return self._real_repository._find_inconsistent_revision_parents(
2695
1732
        providers = [self._unstacked_provider]
2696
1733
        if other is not None:
2697
1734
            providers.insert(0, other)
2698
 
        return graph.StackedParentsProvider(_LazyListJoin(
2699
 
            providers, self._fallback_repositories))
 
1735
        providers.extend(r._make_parents_provider() for r in
 
1736
                         self._fallback_repositories)
 
1737
        return graph.StackedParentsProvider(providers)
2700
1738
 
2701
1739
    def _serialise_search_recipe(self, recipe):
2702
1740
        """Serialise a graph search recipe.
2710
1748
        return '\n'.join((start_keys, stop_keys, count))
2711
1749
 
2712
1750
    def _serialise_search_result(self, search_result):
2713
 
        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)]
2714
1757
        return '\n'.join(parts)
2715
1758
 
2716
1759
    def autopack(self):
2726
1769
            raise errors.UnexpectedSmartServerResponse(response)
2727
1770
 
2728
1771
 
2729
 
class RemoteStreamSink(vf_repository.StreamSink):
 
1772
class RemoteStreamSink(repository.StreamSink):
2730
1773
 
2731
1774
    def _insert_real(self, stream, src_format, resume_tokens):
2732
1775
        self.target_repo._ensure_real()
2833
1876
        self._last_substream and self._last_stream so that the stream can be
2834
1877
        resumed by _resume_stream_with_vfs.
2835
1878
        """
2836
 
 
 
1879
                    
2837
1880
        stream_iter = iter(stream)
2838
1881
        for substream_kind, substream in stream_iter:
2839
1882
            if substream_kind == 'inventory-deltas':
2842
1885
                return
2843
1886
            else:
2844
1887
                yield substream_kind, substream
2845
 
 
2846
 
 
2847
 
class RemoteStreamSource(vf_repository.StreamSource):
 
1888
            
 
1889
 
 
1890
class RemoteStreamSource(repository.StreamSource):
2848
1891
    """Stream data from a remote server."""
2849
1892
 
2850
1893
    def get_stream(self, search):
2871
1914
 
2872
1915
    def _real_stream(self, repo, search):
2873
1916
        """Get a stream for search from repo.
2874
 
 
2875
 
        This never called RemoteStreamSource.get_stream, and is a helper
2876
 
        for RemoteStreamSource._get_stream to allow getting a stream
 
1917
        
 
1918
        This never called RemoteStreamSource.get_stream, and is a heler
 
1919
        for RemoteStreamSource._get_stream to allow getting a stream 
2877
1920
        reliably whether fallback back because of old servers or trying
2878
1921
        to stream from a non-RemoteRepository (which the stacked support
2879
1922
        code will do).
2910
1953
        candidate_verbs = [
2911
1954
            ('Repository.get_stream_1.19', (1, 19)),
2912
1955
            ('Repository.get_stream', (1, 13))]
2913
 
 
2914
1956
        found_verb = False
2915
1957
        for verb, version in candidate_verbs:
2916
1958
            if medium._is_remote_before(version):
2920
1962
                    verb, args, search_bytes)
2921
1963
            except errors.UnknownSmartMethod:
2922
1964
                medium._remember_remote_is_before(version)
2923
 
            except errors.UnknownErrorFromSmartServer, e:
2924
 
                if isinstance(search, vf_search.EverythingResult):
2925
 
                    error_verb = e.error_from_smart_server.error_verb
2926
 
                    if error_verb == 'BadSearch':
2927
 
                        # Pre-2.4 servers don't support this sort of search.
2928
 
                        # XXX: perhaps falling back to VFS on BadSearch is a
2929
 
                        # good idea in general?  It might provide a little bit
2930
 
                        # of protection against client-side bugs.
2931
 
                        medium._remember_remote_is_before((2, 4))
2932
 
                        break
2933
 
                raise
2934
1965
            else:
2935
1966
                response_tuple, response_handler = response
2936
1967
                found_verb = True
2940
1971
        if response_tuple[0] != 'ok':
2941
1972
            raise errors.UnexpectedSmartServerResponse(response_tuple)
2942
1973
        byte_stream = response_handler.read_streamed_body()
2943
 
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream,
2944
 
            self._record_counter)
 
1974
        src_format, stream = smart_repo._byte_stream_to_stream(byte_stream)
2945
1975
        if src_format.network_name() != repo._format.network_name():
2946
1976
            raise AssertionError(
2947
1977
                "Mismatched RemoteRepository and stream src %r, %r" % (
3019
2049
 
3020
2050
    def _ensure_real(self):
3021
2051
        if self._custom_format is None:
3022
 
            try:
3023
 
                self._custom_format = branch.network_format_registry.get(
3024
 
                    self._network_name)
3025
 
            except KeyError:
3026
 
                raise errors.UnknownFormatError(kind='branch',
3027
 
                    format=self._network_name)
 
2052
            self._custom_format = branch.network_format_registry.get(
 
2053
                self._network_name)
3028
2054
 
3029
2055
    def get_format_description(self):
3030
2056
        self._ensure_real()
3037
2063
        return a_bzrdir.open_branch(name=name, 
3038
2064
            ignore_fallbacks=ignore_fallbacks)
3039
2065
 
3040
 
    def _vfs_initialize(self, a_bzrdir, name, append_revisions_only):
 
2066
    def _vfs_initialize(self, a_bzrdir, name):
3041
2067
        # Initialisation when using a local bzrdir object, or a non-vfs init
3042
2068
        # method is not available on the server.
3043
2069
        # self._custom_format is always set - the start of initialize ensures
3045
2071
        if isinstance(a_bzrdir, RemoteBzrDir):
3046
2072
            a_bzrdir._ensure_real()
3047
2073
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
3048
 
                name, append_revisions_only=append_revisions_only)
 
2074
                name)
3049
2075
        else:
3050
2076
            # We assume the bzrdir is parameterised; it may not be.
3051
 
            result = self._custom_format.initialize(a_bzrdir, name,
3052
 
                append_revisions_only=append_revisions_only)
 
2077
            result = self._custom_format.initialize(a_bzrdir, name)
3053
2078
        if (isinstance(a_bzrdir, RemoteBzrDir) and
3054
2079
            not isinstance(result, RemoteBranch)):
3055
2080
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3056
2081
                                  name=name)
3057
2082
        return result
3058
2083
 
3059
 
    def initialize(self, a_bzrdir, name=None, repository=None,
3060
 
                   append_revisions_only=None):
 
2084
    def initialize(self, a_bzrdir, name=None):
3061
2085
        # 1) get the network name to use.
3062
2086
        if self._custom_format:
3063
2087
            network_name = self._custom_format.network_name()
3064
2088
        else:
3065
2089
            # Select the current bzrlib default and ask for that.
3066
 
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
 
2090
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
3067
2091
            reference_format = reference_bzrdir_format.get_branch_format()
3068
2092
            self._custom_format = reference_format
3069
2093
            network_name = reference_format.network_name()
3070
2094
        # Being asked to create on a non RemoteBzrDir:
3071
2095
        if not isinstance(a_bzrdir, RemoteBzrDir):
3072
 
            return self._vfs_initialize(a_bzrdir, name=name,
3073
 
                append_revisions_only=append_revisions_only)
 
2096
            return self._vfs_initialize(a_bzrdir, name=name)
3074
2097
        medium = a_bzrdir._client._medium
3075
2098
        if medium._is_remote_before((1, 13)):
3076
 
            return self._vfs_initialize(a_bzrdir, name=name,
3077
 
                append_revisions_only=append_revisions_only)
 
2099
            return self._vfs_initialize(a_bzrdir, name=name)
3078
2100
        # Creating on a remote bzr dir.
3079
2101
        # 2) try direct creation via RPC
3080
2102
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
3087
2109
        except errors.UnknownSmartMethod:
3088
2110
            # Fallback - use vfs methods
3089
2111
            medium._remember_remote_is_before((1, 13))
3090
 
            return self._vfs_initialize(a_bzrdir, name=name,
3091
 
                    append_revisions_only=append_revisions_only)
 
2112
            return self._vfs_initialize(a_bzrdir, name=name)
3092
2113
        if response[0] != 'ok':
3093
2114
            raise errors.UnexpectedSmartServerResponse(response)
3094
2115
        # Turn the response into a RemoteRepository object.
3095
2116
        format = RemoteBranchFormat(network_name=response[1])
3096
2117
        repo_format = response_tuple_to_repo_format(response[3:])
3097
 
        repo_path = response[2]
3098
 
        if repository is not None:
3099
 
            remote_repo_url = urlutils.join(a_bzrdir.user_url, repo_path)
3100
 
            url_diff = urlutils.relative_url(repository.user_url,
3101
 
                    remote_repo_url)
3102
 
            if url_diff != '.':
3103
 
                raise AssertionError(
3104
 
                    'repository.user_url %r does not match URL from server '
3105
 
                    'response (%r + %r)'
3106
 
                    % (repository.user_url, a_bzrdir.user_url, repo_path))
3107
 
            remote_repo = repository
 
2118
        if response[2] == '':
 
2119
            repo_bzrdir = a_bzrdir
3108
2120
        else:
3109
 
            if repo_path == '':
3110
 
                repo_bzrdir = a_bzrdir
3111
 
            else:
3112
 
                repo_bzrdir = RemoteBzrDir(
3113
 
                    a_bzrdir.root_transport.clone(repo_path), a_bzrdir._format,
3114
 
                    a_bzrdir._client)
3115
 
            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)
3116
2125
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
3117
2126
            format=format, setup_stacking=False, name=name)
3118
 
        if append_revisions_only:
3119
 
            remote_branch.set_append_revisions_only(append_revisions_only)
3120
2127
        # XXX: We know this is a new branch, so it must have revno 0, revid
3121
2128
        # NULL_REVISION. Creating the branch locked would make this be unable
3122
2129
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
3141
2148
        self._ensure_real()
3142
2149
        return self._custom_format.supports_set_append_revisions_only()
3143
2150
 
3144
 
    def _use_default_local_heads_to_fetch(self):
3145
 
        # If the branch format is a metadir format *and* its heads_to_fetch
3146
 
        # implementation is not overridden vs the base class, we can use the
3147
 
        # base class logic rather than use the heads_to_fetch RPC.  This is
3148
 
        # usually cheaper in terms of net round trips, as the last-revision and
3149
 
        # tags info fetched is cached and would be fetched anyway.
3150
 
        self._ensure_real()
3151
 
        if isinstance(self._custom_format, branch.BranchFormatMetadir):
3152
 
            branch_class = self._custom_format._branch_class()
3153
 
            heads_to_fetch_impl = branch_class.heads_to_fetch.im_func
3154
 
            if heads_to_fetch_impl is branch.Branch.heads_to_fetch.im_func:
3155
 
                return True
3156
 
        return False
3157
 
 
3158
 
 
3159
 
class RemoteBranchStore(config.IniFileStore):
3160
 
    """Branch store which attempts to use HPSS calls to retrieve branch store.
3161
 
 
3162
 
    Note that this is specific to bzr-based formats.
3163
 
    """
3164
 
 
3165
 
    def __init__(self, branch):
3166
 
        super(RemoteBranchStore, self).__init__()
3167
 
        self.branch = branch
3168
 
        self.id = "branch"
3169
 
        self._real_store = None
3170
 
 
3171
 
    def lock_write(self, token=None):
3172
 
        return self.branch.lock_write(token)
3173
 
 
3174
 
    def unlock(self):
3175
 
        return self.branch.unlock()
3176
 
 
3177
 
    @needs_write_lock
3178
 
    def save(self):
3179
 
        # We need to be able to override the undecorated implementation
3180
 
        self.save_without_locking()
3181
 
 
3182
 
    def save_without_locking(self):
3183
 
        super(RemoteBranchStore, self).save()
3184
 
 
3185
 
    def external_url(self):
3186
 
        return self.branch.user_url
3187
 
 
3188
 
    def _load_content(self):
3189
 
        path = self.branch._remote_path()
3190
 
        try:
3191
 
            response, handler = self.branch._call_expecting_body(
3192
 
                'Branch.get_config_file', path)
3193
 
        except errors.UnknownSmartMethod:
3194
 
            self._ensure_real()
3195
 
            return self._real_store._load_content()
3196
 
        if len(response) and response[0] != 'ok':
3197
 
            raise errors.UnexpectedSmartServerResponse(response)
3198
 
        return handler.read_body_bytes()
3199
 
 
3200
 
    def _save_content(self, content):
3201
 
        path = self.branch._remote_path()
3202
 
        try:
3203
 
            response, handler = self.branch._call_with_body_bytes_expecting_body(
3204
 
                'Branch.put_config_file', (path,
3205
 
                    self.branch._lock_token, self.branch._repo_lock_token),
3206
 
                content)
3207
 
        except errors.UnknownSmartMethod:
3208
 
            self._ensure_real()
3209
 
            return self._real_store._save_content(content)
3210
 
        handler.cancel_read_body()
3211
 
        if response != ('ok', ):
3212
 
            raise errors.UnexpectedSmartServerResponse(response)
3213
 
 
3214
 
    def _ensure_real(self):
3215
 
        self.branch._ensure_real()
3216
 
        if self._real_store is None:
3217
 
            self._real_store = config.BranchStore(self.branch)
3218
 
 
3219
2151
 
3220
2152
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
3221
2153
    """Branch stored on a server accessed by HPSS RPC.
3224
2156
    """
3225
2157
 
3226
2158
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
3227
 
        _client=None, format=None, setup_stacking=True, name=None,
3228
 
        possible_transports=None):
 
2159
        _client=None, format=None, setup_stacking=True, name=None):
3229
2160
        """Create a RemoteBranch instance.
3230
2161
 
3231
2162
        :param real_branch: An optional local implementation of the branch
3296
2227
            hook(self)
3297
2228
        self._is_stacked = False
3298
2229
        if setup_stacking:
3299
 
            self._setup_stacking(possible_transports)
 
2230
            self._setup_stacking()
3300
2231
 
3301
 
    def _setup_stacking(self, possible_transports):
 
2232
    def _setup_stacking(self):
3302
2233
        # configure stacking into the remote repository, by reading it from
3303
2234
        # the vfs branch.
3304
2235
        try:
3307
2238
            errors.UnstackableRepositoryFormat), e:
3308
2239
            return
3309
2240
        self._is_stacked = True
3310
 
        if possible_transports is None:
3311
 
            possible_transports = []
3312
 
        else:
3313
 
            possible_transports = list(possible_transports)
3314
 
        possible_transports.append(self.bzrdir.root_transport)
3315
 
        self._activate_fallback_location(fallback_url,
3316
 
            possible_transports=possible_transports)
 
2241
        self._activate_fallback_location(fallback_url)
3317
2242
 
3318
2243
    def _get_config(self):
3319
2244
        return RemoteBranchConfig(self)
3320
2245
 
3321
 
    def _get_config_store(self):
3322
 
        return RemoteBranchStore(self)
3323
 
 
3324
2246
    def _get_real_transport(self):
3325
2247
        # if we try vfs access, return the real branch's vfs transport
3326
2248
        self._ensure_real()
3389
2311
                self.bzrdir, self._client)
3390
2312
        return self._control_files
3391
2313
 
 
2314
    def _get_checkout_format(self):
 
2315
        self._ensure_real()
 
2316
        return self._real_branch._get_checkout_format()
 
2317
 
3392
2318
    def get_physical_lock_status(self):
3393
2319
        """See Branch.get_physical_lock_status()."""
3394
 
        try:
3395
 
            response = self._client.call('Branch.get_physical_lock_status',
3396
 
                self._remote_path())
3397
 
        except errors.UnknownSmartMethod:
3398
 
            self._ensure_real()
3399
 
            return self._real_branch.get_physical_lock_status()
3400
 
        if response[0] not in ('yes', 'no'):
3401
 
            raise errors.UnexpectedSmartServerResponse(response)
3402
 
        return (response[0] == 'yes')
 
2320
        # should be an API call to the server, as branches must be lockable.
 
2321
        self._ensure_real()
 
2322
        return self._real_branch.get_physical_lock_status()
3403
2323
 
3404
2324
    def get_stacked_on_url(self):
3405
2325
        """Get the URL this branch is stacked against.
3432
2352
            self._is_stacked = False
3433
2353
        else:
3434
2354
            self._is_stacked = True
3435
 
 
 
2355
        
3436
2356
    def _vfs_get_tags_bytes(self):
3437
2357
        self._ensure_real()
3438
2358
        return self._real_branch._get_tags_bytes()
3439
2359
 
3440
 
    @needs_read_lock
3441
2360
    def _get_tags_bytes(self):
3442
 
        if self._tags_bytes is None:
3443
 
            self._tags_bytes = self._get_tags_bytes_via_hpss()
3444
 
        return self._tags_bytes
3445
 
 
3446
 
    def _get_tags_bytes_via_hpss(self):
3447
2361
        medium = self._client._medium
3448
2362
        if medium._is_remote_before((1, 13)):
3449
2363
            return self._vfs_get_tags_bytes()
3459
2373
        return self._real_branch._set_tags_bytes(bytes)
3460
2374
 
3461
2375
    def _set_tags_bytes(self, bytes):
3462
 
        if self.is_locked():
3463
 
            self._tags_bytes = bytes
3464
2376
        medium = self._client._medium
3465
2377
        if medium._is_remote_before((1, 18)):
3466
2378
            self._vfs_set_tags_bytes(bytes)
3475
2387
            self._vfs_set_tags_bytes(bytes)
3476
2388
 
3477
2389
    def lock_read(self):
3478
 
        """Lock the branch for read operations.
3479
 
 
3480
 
        :return: A bzrlib.lock.LogicalLockResult.
3481
 
        """
3482
2390
        self.repository.lock_read()
3483
2391
        if not self._lock_mode:
3484
2392
            self._note_lock('r')
3488
2396
                self._real_branch.lock_read()
3489
2397
        else:
3490
2398
            self._lock_count += 1
3491
 
        return lock.LogicalLockResult(self.unlock)
3492
2399
 
3493
2400
    def _remote_lock_write(self, token):
3494
2401
        if token is None:
3495
2402
            branch_token = repo_token = ''
3496
2403
        else:
3497
2404
            branch_token = token
3498
 
            repo_token = self.repository.lock_write().repository_token
 
2405
            repo_token = self.repository.lock_write()
3499
2406
            self.repository.unlock()
3500
2407
        err_context = {'token': token}
3501
 
        try:
3502
 
            response = self._call(
3503
 
                'Branch.lock_write', self._remote_path(), branch_token,
3504
 
                repo_token or '', **err_context)
3505
 
        except errors.LockContention, e:
3506
 
            # The LockContention from the server doesn't have any
3507
 
            # information about the lock_url. We re-raise LockContention
3508
 
            # with valid lock_url.
3509
 
            raise errors.LockContention('(remote lock)',
3510
 
                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)
3511
2411
        if response[0] != 'ok':
3512
2412
            raise errors.UnexpectedSmartServerResponse(response)
3513
2413
        ok, branch_token, repo_token = response
3534
2434
            self._lock_mode = 'w'
3535
2435
            self._lock_count = 1
3536
2436
        elif self._lock_mode == 'r':
3537
 
            raise errors.ReadOnlyError(self)
 
2437
            raise errors.ReadOnlyTransaction
3538
2438
        else:
3539
2439
            if token is not None:
3540
2440
                # A token was given to lock_write, and we're relocking, so
3545
2445
            self._lock_count += 1
3546
2446
            # Re-lock the repository too.
3547
2447
            self.repository.lock_write(self._repo_lock_token)
3548
 
        return BranchWriteLockResult(self.unlock, self._lock_token or None)
 
2448
        return self._lock_token or None
3549
2449
 
3550
2450
    def _unlock(self, branch_token, repo_token):
3551
2451
        err_context = {'token': str((branch_token, repo_token))}
3589
2489
            self.repository.unlock()
3590
2490
 
3591
2491
    def break_lock(self):
3592
 
        try:
3593
 
            response = self._call(
3594
 
                'Branch.break_lock', self._remote_path())
3595
 
        except errors.UnknownSmartMethod:
3596
 
            self._ensure_real()
3597
 
            return self._real_branch.break_lock()
3598
 
        if response != ('ok',):
3599
 
            raise errors.UnexpectedSmartServerResponse(response)
 
2492
        self._ensure_real()
 
2493
        return self._real_branch.break_lock()
3600
2494
 
3601
2495
    def leave_lock_in_place(self):
3602
2496
        if not self._lock_token:
3626
2520
            missing_parent = parent_map[missing_parent]
3627
2521
        raise errors.RevisionNotPresent(missing_parent, self.repository)
3628
2522
 
3629
 
    def _read_last_revision_info(self):
 
2523
    def _last_revision_info(self):
3630
2524
        response = self._call('Branch.last_revision_info', self._remote_path())
3631
2525
        if response[0] != 'ok':
3632
2526
            raise SmartProtocolError('unexpected response code %s' % (response,))
3695
2589
            raise errors.UnexpectedSmartServerResponse(response)
3696
2590
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
3697
2591
 
3698
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
3699
2592
    @needs_write_lock
3700
2593
    def set_revision_history(self, rev_history):
3701
 
        """See Branch.set_revision_history."""
3702
 
        self._set_revision_history(rev_history)
3703
 
 
3704
 
    @needs_write_lock
3705
 
    def _set_revision_history(self, rev_history):
3706
2594
        # Send just the tip revision of the history; the server will generate
3707
2595
        # the full history from that.  If the revision doesn't exist in this
3708
2596
        # branch, NoSuchRevision will be raised.
3766
2654
            _override_hook_target=self, **kwargs)
3767
2655
 
3768
2656
    @needs_read_lock
3769
 
    def push(self, target, overwrite=False, stop_revision=None, lossy=False):
 
2657
    def push(self, target, overwrite=False, stop_revision=None):
3770
2658
        self._ensure_real()
3771
2659
        return self._real_branch.push(
3772
 
            target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
 
2660
            target, overwrite=overwrite, stop_revision=stop_revision,
3773
2661
            _override_hook_source_branch=self)
3774
2662
 
3775
2663
    def is_locked(self):
3776
2664
        return self._lock_count >= 1
3777
2665
 
3778
2666
    @needs_read_lock
3779
 
    def revision_id_to_dotted_revno(self, revision_id):
3780
 
        """Given a revision id, return its dotted revno.
3781
 
 
3782
 
        :return: a tuple like (1,) or (400,1,3).
3783
 
        """
3784
 
        try:
3785
 
            response = self._call('Branch.revision_id_to_revno',
3786
 
                self._remote_path(), revision_id)
3787
 
        except errors.UnknownSmartMethod:
3788
 
            self._ensure_real()
3789
 
            return self._real_branch.revision_id_to_dotted_revno(revision_id)
3790
 
        if response[0] == 'ok':
3791
 
            return tuple([int(x) for x in response[1:]])
3792
 
        else:
3793
 
            raise errors.UnexpectedSmartServerResponse(response)
3794
 
 
3795
 
    @needs_read_lock
3796
2667
    def revision_id_to_revno(self, revision_id):
3797
 
        """Given a revision id on the branch mainline, return its revno.
3798
 
 
3799
 
        :return: an integer
3800
 
        """
3801
 
        try:
3802
 
            response = self._call('Branch.revision_id_to_revno',
3803
 
                self._remote_path(), revision_id)
3804
 
        except errors.UnknownSmartMethod:
3805
 
            self._ensure_real()
3806
 
            return self._real_branch.revision_id_to_revno(revision_id)
3807
 
        if response[0] == 'ok':
3808
 
            if len(response) == 2:
3809
 
                return int(response[1])
3810
 
            raise NoSuchRevision(self, revision_id)
3811
 
        else:
3812
 
            raise errors.UnexpectedSmartServerResponse(response)
 
2668
        self._ensure_real()
 
2669
        return self._real_branch.revision_id_to_revno(revision_id)
3813
2670
 
3814
2671
    @needs_write_lock
3815
2672
    def set_last_revision_info(self, revno, revision_id):
3816
2673
        # XXX: These should be returned by the set_last_revision_info verb
3817
2674
        old_revno, old_revid = self.last_revision_info()
3818
2675
        self._run_pre_change_branch_tip_hooks(revno, revision_id)
3819
 
        if not revision_id or not isinstance(revision_id, basestring):
3820
 
            raise errors.InvalidRevisionId(revision_id=revision_id, branch=self)
 
2676
        revision_id = ensure_null(revision_id)
3821
2677
        try:
3822
2678
            response = self._call('Branch.set_last_revision_info',
3823
2679
                self._remote_path(), self._lock_token, self._repo_lock_token,
3852
2708
            except errors.UnknownSmartMethod:
3853
2709
                medium._remember_remote_is_before((1, 6))
3854
2710
        self._clear_cached_state_of_remote_branch_only()
3855
 
        self._set_revision_history(self._lefthand_history(revision_id,
 
2711
        self.set_revision_history(self._lefthand_history(revision_id,
3856
2712
            last_rev=last_rev,other_branch=other_branch))
3857
2713
 
3858
2714
    def set_push_location(self, location):
3859
2715
        self._ensure_real()
3860
2716
        return self._real_branch.set_push_location(location)
3861
2717
 
3862
 
    def heads_to_fetch(self):
3863
 
        if self._format._use_default_local_heads_to_fetch():
3864
 
            # We recognise this format, and its heads-to-fetch implementation
3865
 
            # is the default one (tip + tags).  In this case it's cheaper to
3866
 
            # just use the default implementation rather than a special RPC as
3867
 
            # the tip and tags data is cached.
3868
 
            return branch.Branch.heads_to_fetch(self)
3869
 
        medium = self._client._medium
3870
 
        if medium._is_remote_before((2, 4)):
3871
 
            return self._vfs_heads_to_fetch()
3872
 
        try:
3873
 
            return self._rpc_heads_to_fetch()
3874
 
        except errors.UnknownSmartMethod:
3875
 
            medium._remember_remote_is_before((2, 4))
3876
 
            return self._vfs_heads_to_fetch()
3877
 
 
3878
 
    def _rpc_heads_to_fetch(self):
3879
 
        response = self._call('Branch.heads_to_fetch', self._remote_path())
3880
 
        if len(response) != 2:
3881
 
            raise errors.UnexpectedSmartServerResponse(response)
3882
 
        must_fetch, if_present_fetch = response
3883
 
        return set(must_fetch), set(if_present_fetch)
3884
 
 
3885
 
    def _vfs_heads_to_fetch(self):
3886
 
        self._ensure_real()
3887
 
        return self._real_branch.heads_to_fetch()
3888
 
 
3889
2718
 
3890
2719
class RemoteConfig(object):
3891
2720
    """A Config that reads and writes from smart verbs.
3905
2734
        """
3906
2735
        try:
3907
2736
            configobj = self._get_configobj()
3908
 
            section_obj = None
3909
2737
            if section is None:
3910
2738
                section_obj = configobj
3911
2739
            else:
3912
2740
                try:
3913
2741
                    section_obj = configobj[section]
3914
2742
                except KeyError:
3915
 
                    pass
3916
 
            if section_obj is None:
3917
 
                value = default
3918
 
            else:
3919
 
                value = section_obj.get(name, default)
 
2743
                    return default
 
2744
            return section_obj.get(name, default)
3920
2745
        except errors.UnknownSmartMethod:
3921
 
            value = self._vfs_get_option(name, section, default)
3922
 
        for hook in config.OldConfigHooks['get']:
3923
 
            hook(self, name, value)
3924
 
        return value
 
2746
            return self._vfs_get_option(name, section, default)
3925
2747
 
3926
2748
    def _response_to_configobj(self, response):
3927
2749
        if len(response[0]) and response[0][0] != 'ok':
3928
2750
            raise errors.UnexpectedSmartServerResponse(response)
3929
2751
        lines = response[1].read_body_bytes().splitlines()
3930
 
        conf = config.ConfigObj(lines, encoding='utf-8')
3931
 
        for hook in config.OldConfigHooks['load']:
3932
 
            hook(self)
3933
 
        return conf
 
2752
        return config.ConfigObj(lines, encoding='utf-8')
3934
2753
 
3935
2754
 
3936
2755
class RemoteBranchConfig(RemoteConfig):
3955
2774
        medium = self._branch._client._medium
3956
2775
        if medium._is_remote_before((1, 14)):
3957
2776
            return self._vfs_set_option(value, name, section)
3958
 
        if isinstance(value, dict):
3959
 
            if medium._is_remote_before((2, 2)):
3960
 
                return self._vfs_set_option(value, name, section)
3961
 
            return self._set_config_option_dict(value, name, section)
3962
 
        else:
3963
 
            return self._set_config_option(value, name, section)
3964
 
 
3965
 
    def _set_config_option(self, value, name, section):
3966
2777
        try:
3967
2778
            path = self._branch._remote_path()
3968
2779
            response = self._branch._client.call('Branch.set_config_option',
3969
2780
                path, self._branch._lock_token, self._branch._repo_lock_token,
3970
2781
                value.encode('utf8'), name, section or '')
3971
2782
        except errors.UnknownSmartMethod:
3972
 
            medium = self._branch._client._medium
3973
2783
            medium._remember_remote_is_before((1, 14))
3974
2784
            return self._vfs_set_option(value, name, section)
3975
2785
        if response != ():
3976
2786
            raise errors.UnexpectedSmartServerResponse(response)
3977
2787
 
3978
 
    def _serialize_option_dict(self, option_dict):
3979
 
        utf8_dict = {}
3980
 
        for key, value in option_dict.items():
3981
 
            if isinstance(key, unicode):
3982
 
                key = key.encode('utf8')
3983
 
            if isinstance(value, unicode):
3984
 
                value = value.encode('utf8')
3985
 
            utf8_dict[key] = value
3986
 
        return bencode.bencode(utf8_dict)
3987
 
 
3988
 
    def _set_config_option_dict(self, value, name, section):
3989
 
        try:
3990
 
            path = self._branch._remote_path()
3991
 
            serialised_dict = self._serialize_option_dict(value)
3992
 
            response = self._branch._client.call(
3993
 
                'Branch.set_config_option_dict',
3994
 
                path, self._branch._lock_token, self._branch._repo_lock_token,
3995
 
                serialised_dict, name, section or '')
3996
 
        except errors.UnknownSmartMethod:
3997
 
            medium = self._branch._client._medium
3998
 
            medium._remember_remote_is_before((2, 2))
3999
 
            return self._vfs_set_option(value, name, section)
4000
 
        if response != ():
4001
 
            raise errors.UnexpectedSmartServerResponse(response)
4002
 
 
4003
2788
    def _real_object(self):
4004
2789
        self._branch._ensure_real()
4005
2790
        return self._branch._real_branch
4044
2829
        return self._bzrdir._real_bzrdir
4045
2830
 
4046
2831
 
 
2832
 
4047
2833
def _extract_tar(tar, to_dir):
4048
2834
    """Extract all the contents of a tarfile object.
4049
2835
 
4053
2839
        tar.extract(tarinfo, to_dir)
4054
2840
 
4055
2841
 
4056
 
error_translators = registry.Registry()
4057
 
no_context_error_translators = registry.Registry()
4058
 
 
4059
 
 
4060
2842
def _translate_error(err, **context):
4061
2843
    """Translate an ErrorFromSmartServer into a more useful error.
4062
2844
 
4091
2873
                    'Missing key %r in context %r', key_err.args[0], context)
4092
2874
                raise err
4093
2875
 
4094
 
    try:
4095
 
        translator = error_translators.get(err.error_verb)
4096
 
    except KeyError:
4097
 
        pass
4098
 
    else:
4099
 
        raise translator(err, find, get_path)
4100
 
    try:
4101
 
        translator = no_context_error_translators.get(err.error_verb)
4102
 
    except KeyError:
4103
 
        raise errors.UnknownErrorFromSmartServer(err)
4104
 
    else:
4105
 
        raise translator(err)
4106
 
 
4107
 
 
4108
 
error_translators.register('NoSuchRevision',
4109
 
    lambda err, find, get_path: NoSuchRevision(
4110
 
        find('branch'), err.error_args[0]))
4111
 
error_translators.register('nosuchrevision',
4112
 
    lambda err, find, get_path: NoSuchRevision(
4113
 
        find('repository'), err.error_args[0]))
4114
 
 
4115
 
def _translate_nobranch_error(err, find, get_path):
4116
 
    if len(err.error_args) >= 1:
4117
 
        extra = err.error_args[0]
4118
 
    else:
4119
 
        extra = None
4120
 
    return errors.NotBranchError(path=find('bzrdir').root_transport.base,
4121
 
        detail=extra)
4122
 
 
4123
 
error_translators.register('nobranch', _translate_nobranch_error)
4124
 
error_translators.register('norepository',
4125
 
    lambda err, find, get_path: errors.NoRepositoryPresent(
4126
 
        find('bzrdir')))
4127
 
error_translators.register('UnlockableTransport',
4128
 
    lambda err, find, get_path: errors.UnlockableTransport(
4129
 
        find('bzrdir').root_transport))
4130
 
error_translators.register('TokenMismatch',
4131
 
    lambda err, find, get_path: errors.TokenMismatch(
4132
 
        find('token'), '(remote token)'))
4133
 
error_translators.register('Diverged',
4134
 
    lambda err, find, get_path: errors.DivergedBranches(
4135
 
        find('branch'), find('other_branch')))
4136
 
error_translators.register('NotStacked',
4137
 
    lambda err, find, get_path: errors.NotStacked(branch=find('branch')))
4138
 
 
4139
 
def _translate_PermissionDenied(err, find, get_path):
4140
 
    path = get_path()
4141
 
    if len(err.error_args) >= 2:
4142
 
        extra = err.error_args[1]
4143
 
    else:
4144
 
        extra = None
4145
 
    return errors.PermissionDenied(path, extra=extra)
4146
 
 
4147
 
error_translators.register('PermissionDenied', _translate_PermissionDenied)
4148
 
error_translators.register('ReadError',
4149
 
    lambda err, find, get_path: errors.ReadError(get_path()))
4150
 
error_translators.register('NoSuchFile',
4151
 
    lambda err, find, get_path: errors.NoSuchFile(get_path()))
4152
 
error_translators.register('TokenLockingNotSupported',
4153
 
    lambda err, find, get_path: errors.TokenLockingNotSupported(
4154
 
        find('repository')))
4155
 
error_translators.register('UnsuspendableWriteGroup',
4156
 
    lambda err, find, get_path: errors.UnsuspendableWriteGroup(
4157
 
        repository=find('repository')))
4158
 
error_translators.register('UnresumableWriteGroup',
4159
 
    lambda err, find, get_path: errors.UnresumableWriteGroup(
4160
 
        repository=find('repository'), write_groups=err.error_args[0],
4161
 
        reason=err.error_args[1]))
4162
 
no_context_error_translators.register('IncompatibleRepositories',
4163
 
    lambda err: errors.IncompatibleRepositories(
4164
 
        err.error_args[0], err.error_args[1], err.error_args[2]))
4165
 
no_context_error_translators.register('LockContention',
4166
 
    lambda err: errors.LockContention('(remote lock)'))
4167
 
no_context_error_translators.register('LockFailed',
4168
 
    lambda err: errors.LockFailed(err.error_args[0], err.error_args[1]))
4169
 
no_context_error_translators.register('TipChangeRejected',
4170
 
    lambda err: errors.TipChangeRejected(err.error_args[0].decode('utf8')))
4171
 
no_context_error_translators.register('UnstackableBranchFormat',
4172
 
    lambda err: errors.UnstackableBranchFormat(*err.error_args))
4173
 
no_context_error_translators.register('UnstackableRepositoryFormat',
4174
 
    lambda err: errors.UnstackableRepositoryFormat(*err.error_args))
4175
 
no_context_error_translators.register('FileExists',
4176
 
    lambda err: errors.FileExists(err.error_args[0]))
4177
 
no_context_error_translators.register('DirectoryNotEmpty',
4178
 
    lambda err: errors.DirectoryNotEmpty(err.error_args[0]))
4179
 
 
4180
 
def _translate_short_readv_error(err):
4181
 
    args = err.error_args
4182
 
    return errors.ShortReadvError(args[0], int(args[1]), int(args[2]),
4183
 
        int(args[3]))
4184
 
 
4185
 
no_context_error_translators.register('ShortReadvError',
4186
 
    _translate_short_readv_error)
4187
 
 
4188
 
def _translate_unicode_error(err):
 
2876
    if err.error_verb == 'IncompatibleRepositories':
 
2877
        raise errors.IncompatibleRepositories(err.error_args[0],
 
2878
            err.error_args[1], err.error_args[2])
 
2879
    elif err.error_verb == 'NoSuchRevision':
 
2880
        raise NoSuchRevision(find('branch'), err.error_args[0])
 
2881
    elif err.error_verb == 'nosuchrevision':
 
2882
        raise NoSuchRevision(find('repository'), err.error_args[0])
 
2883
    elif err.error_verb == 'nobranch':
 
2884
        if len(err.error_args) >= 1:
 
2885
            extra = err.error_args[0]
 
2886
        else:
 
2887
            extra = None
 
2888
        raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
 
2889
            detail=extra)
 
2890
    elif err.error_verb == 'norepository':
 
2891
        raise errors.NoRepositoryPresent(find('bzrdir'))
 
2892
    elif err.error_verb == 'LockContention':
 
2893
        raise errors.LockContention('(remote lock)')
 
2894
    elif err.error_verb == 'UnlockableTransport':
 
2895
        raise errors.UnlockableTransport(find('bzrdir').root_transport)
 
2896
    elif err.error_verb == 'LockFailed':
 
2897
        raise errors.LockFailed(err.error_args[0], err.error_args[1])
 
2898
    elif err.error_verb == 'TokenMismatch':
 
2899
        raise errors.TokenMismatch(find('token'), '(remote token)')
 
2900
    elif err.error_verb == 'Diverged':
 
2901
        raise errors.DivergedBranches(find('branch'), find('other_branch'))
 
2902
    elif err.error_verb == 'TipChangeRejected':
 
2903
        raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
 
2904
    elif err.error_verb == 'UnstackableBranchFormat':
 
2905
        raise errors.UnstackableBranchFormat(*err.error_args)
 
2906
    elif err.error_verb == 'UnstackableRepositoryFormat':
 
2907
        raise errors.UnstackableRepositoryFormat(*err.error_args)
 
2908
    elif err.error_verb == 'NotStacked':
 
2909
        raise errors.NotStacked(branch=find('branch'))
 
2910
    elif err.error_verb == 'PermissionDenied':
 
2911
        path = get_path()
 
2912
        if len(err.error_args) >= 2:
 
2913
            extra = err.error_args[1]
 
2914
        else:
 
2915
            extra = None
 
2916
        raise errors.PermissionDenied(path, extra=extra)
 
2917
    elif err.error_verb == 'ReadError':
 
2918
        path = get_path()
 
2919
        raise errors.ReadError(path)
 
2920
    elif err.error_verb == 'NoSuchFile':
 
2921
        path = get_path()
 
2922
        raise errors.NoSuchFile(path)
 
2923
    elif err.error_verb == 'FileExists':
 
2924
        raise errors.FileExists(err.error_args[0])
 
2925
    elif err.error_verb == 'DirectoryNotEmpty':
 
2926
        raise errors.DirectoryNotEmpty(err.error_args[0])
 
2927
    elif err.error_verb == 'ShortReadvError':
 
2928
        args = err.error_args
 
2929
        raise errors.ShortReadvError(
 
2930
            args[0], int(args[1]), int(args[2]), int(args[3]))
 
2931
    elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
4189
2932
        encoding = str(err.error_args[0]) # encoding must always be a string
4190
2933
        val = err.error_args[1]
4191
2934
        start = int(err.error_args[2])
4199
2942
            raise UnicodeDecodeError(encoding, val, start, end, reason)
4200
2943
        elif err.error_verb == 'UnicodeEncodeError':
4201
2944
            raise UnicodeEncodeError(encoding, val, start, end, reason)
4202
 
 
4203
 
no_context_error_translators.register('UnicodeEncodeError',
4204
 
    _translate_unicode_error)
4205
 
no_context_error_translators.register('UnicodeDecodeError',
4206
 
    _translate_unicode_error)
4207
 
no_context_error_translators.register('ReadOnlyError',
4208
 
    lambda err: errors.TransportNotPossible('readonly transport'))
4209
 
no_context_error_translators.register('MemoryError',
4210
 
    lambda err: errors.BzrError("remote server out of memory\n"
4211
 
        "Retry non-remotely, or contact the server admin for details."))
4212
 
no_context_error_translators.register('RevisionNotPresent',
4213
 
    lambda err: errors.RevisionNotPresent(err.error_args[0], err.error_args[1]))
4214
 
 
4215
 
no_context_error_translators.register('BzrCheckError',
4216
 
    lambda err: errors.BzrCheckError(msg=err.error_args[0]))
4217
 
 
 
2945
    elif err.error_verb == 'ReadOnlyError':
 
2946
        raise errors.TransportNotPossible('readonly transport')
 
2947
    raise errors.UnknownErrorFromSmartServer(err)