~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/remote.py

  • Committer: Martin Pool
  • Date: 2010-06-02 05:03:31 UTC
  • mto: This revision was merged to the branch mainline in revision 5279.
  • Revision ID: mbp@canonical.com-20100602050331-n2p1qt8hfsahspnv
Correct more sloppy use of the term 'Linux'

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
 
    )
 
35
)
46
36
from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult
 
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.repository import RepositoryWriteLockResult
 
47
from bzrlib.trace import mutter, note, warning
65
48
 
66
49
 
67
50
class _RpcHelper(object):
104
87
    return format
105
88
 
106
89
 
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):
 
90
# Note: RemoteBzrDirFormat is in bzrdir.py
 
91
 
 
92
class RemoteBzrDir(BzrDir, _RpcHelper):
408
93
    """Control directory on a remote server, accessed via bzr:// or similar."""
409
94
 
410
95
    def __init__(self, transport, format, _client=None, _force_probe=False):
413
98
        :param _client: Private parameter for testing. Disables probing and the
414
99
            use of a real bzrdir.
415
100
        """
416
 
        _mod_bzrdir.BzrDir.__init__(self, transport, format)
 
101
        BzrDir.__init__(self, transport, format)
417
102
        # this object holds a delegated bzrdir that uses file-level operations
418
103
        # to talk to the other side
419
104
        self._real_bzrdir = None
479
164
                import traceback
480
165
                warning('VFS BzrDir access triggered\n%s',
481
166
                    ''.join(traceback.format_stack()))
482
 
            self._real_bzrdir = _mod_bzrdir.BzrDir.open_from_transport(
 
167
            self._real_bzrdir = BzrDir.open_from_transport(
483
168
                self.root_transport, _server_formats=False)
484
169
            self._format._network_name = \
485
170
                self._real_bzrdir._format.network_name()
491
176
        # Prevent aliasing problems in the next_open_branch_result cache.
492
177
        # See create_branch for rationale.
493
178
        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
 
179
        return BzrDir.break_lock(self)
537
180
 
538
181
    def _vfs_cloning_metadir(self, require_stacking=False):
539
182
        self._ensure_real()
570
213
        if len(branch_info) != 2:
571
214
            raise errors.UnexpectedSmartServerResponse(response)
572
215
        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
 
 
 
216
        format = bzrdir.network_format_registry.get(control_name)
578
217
        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)
 
218
            format.repository_format = repository.network_format_registry.get(
 
219
                repo_name)
585
220
        if branch_ref == 'ref':
586
221
            # XXX: we need possible_transports here to avoid reopening the
587
222
            # connection to the referenced location
588
 
            ref_bzrdir = _mod_bzrdir.BzrDir.open(branch_name)
 
223
            ref_bzrdir = BzrDir.open(branch_name)
589
224
            branch_format = ref_bzrdir.cloning_metadir().get_branch_format()
590
225
            format.set_branch_format(branch_format)
591
226
        elif branch_ref == 'branch':
592
227
            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)
 
228
                format.set_branch_format(
 
229
                    branch.network_format_registry.get(branch_name))
600
230
        else:
601
231
            raise errors.UnexpectedSmartServerResponse(response)
602
232
        return format
612
242
 
613
243
    def destroy_repository(self):
614
244
        """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,))
 
245
        self._ensure_real()
 
246
        self._real_bzrdir.destroy_repository()
624
247
 
625
 
    def create_branch(self, name=None, repository=None,
626
 
                      append_revisions_only=None):
 
248
    def create_branch(self, name=None):
627
249
        # as per meta1 formats - just delegate to the format object which may
628
250
        # be parameterised.
629
251
        real_branch = self._format.get_branch_format().initialize(self,
630
 
            name=name, repository=repository,
631
 
            append_revisions_only=append_revisions_only)
 
252
            name=name)
632
253
        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)
 
254
            result = RemoteBranch(self, self.find_repository(), real_branch,
 
255
                                  name=name)
638
256
        else:
639
257
            result = real_branch
640
258
        # BzrDir.clone_on_transport() uses the result of create_branch but does
648
266
 
649
267
    def destroy_branch(self, name=None):
650
268
        """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
 
269
        self._ensure_real()
 
270
        self._real_bzrdir.destroy_branch(name=name)
663
271
        self._next_open_branch_result = None
664
 
        if response[0] != 'ok':
665
 
            raise SmartProtocolError('unexpected response code %s' % (response,))
666
272
 
667
 
    def create_workingtree(self, revision_id=None, from_branch=None,
668
 
        accelerator_tree=None, hardlink=False):
 
273
    def create_workingtree(self, revision_id=None, from_branch=None):
669
274
        raise errors.NotLocalUrl(self.transport.base)
670
275
 
671
276
    def find_branch_format(self, name=None):
722
327
        return None, self.open_branch(name=name)
723
328
 
724
329
    def open_branch(self, name=None, unsupported=False,
725
 
                    ignore_fallbacks=False, possible_transports=None):
 
330
                    ignore_fallbacks=False):
726
331
        if unsupported:
727
332
            raise NotImplementedError('unsupported flag support not implemented yet.')
728
333
        if self._next_open_branch_result is not None:
735
340
            # a branch reference, use the existing BranchReference logic.
736
341
            format = BranchReferenceFormat()
737
342
            return format.open(self, name=name, _found=True,
738
 
                location=response[1], ignore_fallbacks=ignore_fallbacks,
739
 
                possible_transports=possible_transports)
 
343
                location=response[1], ignore_fallbacks=ignore_fallbacks)
740
344
        branch_format_name = response[1]
741
345
        if not branch_format_name:
742
346
            branch_format_name = None
743
347
        format = RemoteBranchFormat(network_name=branch_format_name)
744
348
        return RemoteBranch(self, self.find_repository(), format=format,
745
 
            setup_stacking=not ignore_fallbacks, name=name,
746
 
            possible_transports=possible_transports)
 
349
            setup_stacking=not ignore_fallbacks, name=name)
747
350
 
748
351
    def _open_repo_v1(self, path):
749
352
        verb = 'BzrDir.find_repository'
812
415
 
813
416
    def has_workingtree(self):
814
417
        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')
 
418
            self._ensure_real()
 
419
            self._has_working_tree = self._real_bzrdir.has_workingtree()
825
420
        return self._has_working_tree
826
421
 
827
422
    def open_workingtree(self, recommend_upgrade=True):
832
427
 
833
428
    def _path_for_remote_call(self, client):
834
429
        """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]
 
430
        return client.remote_path_from_transport(self.root_transport)
837
431
 
838
432
    def get_branch_transport(self, branch_format, name=None):
839
433
        self._ensure_real()
851
445
        """Upgrading of remote bzrdirs is not supported yet."""
852
446
        return False
853
447
 
854
 
    def needs_format_conversion(self, format):
 
448
    def needs_format_conversion(self, format=None):
855
449
        """Upgrading of remote bzrdirs is not supported yet."""
 
450
        if format is None:
 
451
            symbol_versioning.warn(symbol_versioning.deprecated_in((1, 13, 0))
 
452
                % 'needs_format_conversion(format=None)')
856
453
        return False
857
454
 
 
455
    def clone(self, url, revision_id=None, force_new_repo=False,
 
456
              preserve_stacking=False):
 
457
        self._ensure_real()
 
458
        return self._real_bzrdir.clone(url, revision_id=revision_id,
 
459
            force_new_repo=force_new_repo, preserve_stacking=preserve_stacking)
 
460
 
858
461
    def _get_config(self):
859
462
        return RemoteBzrDirConfig(self)
860
463
 
861
 
    def _get_config_store(self):
862
 
        return RemoteControlStore(self)
863
 
 
864
 
 
865
 
class RemoteRepositoryFormat(vf_repository.VersionedFileRepositoryFormat):
 
464
 
 
465
class RemoteRepositoryFormat(repository.RepositoryFormat):
866
466
    """Format for repositories accessed over a _SmartClient.
867
467
 
868
468
    Instances of this repository are represented by RemoteRepository
883
483
    """
884
484
 
885
485
    _matchingbzrdir = RemoteBzrDirFormat()
886
 
    supports_full_versioned_files = True
887
 
    supports_leaving_lock = True
888
486
 
889
487
    def __init__(self):
890
 
        _mod_repository.RepositoryFormat.__init__(self)
 
488
        repository.RepositoryFormat.__init__(self)
891
489
        self._custom_format = None
892
490
        self._network_name = None
893
491
        self._creating_bzrdir = None
894
 
        self._revision_graph_can_have_wrong_parents = None
895
492
        self._supports_chks = None
896
493
        self._supports_external_lookups = None
897
494
        self._supports_tree_reference = None
898
 
        self._supports_funky_characters = None
899
 
        self._supports_nesting_repositories = None
900
495
        self._rich_root_data = None
901
496
 
902
497
    def __repr__(self):
931
526
        return self._supports_external_lookups
932
527
 
933
528
    @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
529
    def supports_tree_reference(self):
951
530
        if self._supports_tree_reference is None:
952
531
            self._ensure_real()
954
533
                self._custom_format.supports_tree_reference
955
534
        return self._supports_tree_reference
956
535
 
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
536
    def _vfs_initialize(self, a_bzrdir, shared):
966
537
        """Helper for common code in initialize."""
967
538
        if self._custom_format:
1002
573
            network_name = self._network_name
1003
574
        else:
1004
575
            # Select the current bzrlib default and ask for that.
1005
 
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
 
576
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
1006
577
            reference_format = reference_bzrdir_format.repository_format
1007
578
            network_name = reference_format.network_name()
1008
579
        # 2) try direct creation via RPC
1034
605
 
1035
606
    def _ensure_real(self):
1036
607
        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)
 
608
            self._custom_format = repository.network_format_registry.get(
 
609
                self._network_name)
1043
610
 
1044
611
    @property
1045
612
    def _fetch_order(self):
1080
647
        return self._custom_format._serializer
1081
648
 
1082
649
 
1083
 
class RemoteRepository(_mod_repository.Repository, _RpcHelper,
1084
 
        lock._RelockDebugMixin):
 
650
class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
 
651
    bzrdir.ControlComponent):
1085
652
    """Repository accessed over rpc.
1086
653
 
1087
654
    For the moment most operations are performed using local transport-backed
1111
678
        self._format = format
1112
679
        self._lock_mode = None
1113
680
        self._lock_token = None
1114
 
        self._write_group_tokens = None
1115
681
        self._lock_count = 0
1116
682
        self._leave_lock = False
1117
683
        # Cache of revision parents; misses are cached during read locks, and
1141
707
        # transport, but I'm not sure it's worth making this method
1142
708
        # optional -- mbp 2010-04-21
1143
709
        return self.bzrdir.get_repository_transport(None)
1144
 
 
 
710
        
1145
711
    def __str__(self):
1146
712
        return "%s(%s)" % (self.__class__.__name__, self.base)
1147
713
 
1157
723
 
1158
724
        :param suppress_errors: see Repository.abort_write_group.
1159
725
        """
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
 
726
        self._ensure_real()
 
727
        return self._real_repository.abort_write_group(
 
728
            suppress_errors=suppress_errors)
1184
729
 
1185
730
    @property
1186
731
    def chk_bytes(self):
1200
745
        for older plugins that don't use e.g. the CommitBuilder
1201
746
        facility.
1202
747
        """
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
 
748
        self._ensure_real()
 
749
        return self._real_repository.commit_write_group()
1214
750
 
1215
751
    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
 
752
        self._ensure_real()
 
753
        return self._real_repository.resume_write_group(tokens)
1228
754
 
1229
755
    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
 
756
        self._ensure_real()
 
757
        return self._real_repository.suspend_write_group()
1235
758
 
1236
759
    def get_missing_parent_inventories(self, check_for_missing_texts=True):
1237
760
        self._ensure_real()
1298
821
    def find_text_key_references(self):
1299
822
        """Find the text key references within the repository.
1300
823
 
 
824
        :return: a dictionary mapping (file_id, revision_id) tuples to altered file-ids to an iterable of
 
825
        revision_ids. Each altered file-ids has the exact revision_ids that
 
826
        altered it listed explicitly.
1301
827
        :return: A dictionary mapping text keys ((fileid, revision_id) tuples)
1302
828
            to whether they were referred to by the inventory of the
1303
829
            revision_id that they contain. The inventory texts from all present
1321
847
        """Private method for using with old (< 1.2) servers to fallback."""
1322
848
        if revision_id is None:
1323
849
            revision_id = ''
1324
 
        elif _mod_revision.is_null(revision_id):
 
850
        elif revision.is_null(revision_id):
1325
851
            return {}
1326
852
 
1327
853
        path = self.bzrdir._path_for_remote_call(self._client)
1351
877
        return RemoteStreamSource(self, to_format)
1352
878
 
1353
879
    @needs_read_lock
1354
 
    def get_file_graph(self):
1355
 
        return graph.Graph(self.texts)
1356
 
 
1357
 
    @needs_read_lock
1358
880
    def has_revision(self, revision_id):
1359
881
        """True if this repository has a copy of the revision."""
1360
882
        # Copy of bzrlib.repository.Repository.has_revision
1412
934
        """See Repository.gather_stats()."""
1413
935
        path = self.bzrdir._path_for_remote_call(self._client)
1414
936
        # revid can be None to indicate no revisions, not just NULL_REVISION
1415
 
        if revid is None or _mod_revision.is_null(revid):
 
937
        if revid is None or revision.is_null(revid):
1416
938
            fmt_revid = ''
1417
939
        else:
1418
940
            fmt_revid = revid
1447
969
 
1448
970
    def get_physical_lock_status(self):
1449
971
        """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')
 
972
        # should be an API call to the server.
 
973
        self._ensure_real()
 
974
        return self._real_repository.get_physical_lock_status()
1459
975
 
1460
976
    def is_in_write_group(self):
1461
977
        """Return True if there is an open write group.
1462
978
 
1463
979
        write groups are only applicable locally for the smart server..
1464
980
        """
1465
 
        if self._write_group_tokens is not None:
1466
 
            return True
1467
981
        if self._real_repository:
1468
982
            return self._real_repository.is_in_write_group()
1469
983
 
1604
1118
            self._real_repository.lock_write(self._lock_token)
1605
1119
        elif self._lock_mode == 'r':
1606
1120
            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
1121
 
1612
1122
    def start_write_group(self):
1613
1123
        """Start a write group on the decorated repository.
1617
1127
        for older plugins that don't use e.g. the CommitBuilder
1618
1128
        facility.
1619
1129
        """
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]
 
1130
        self._ensure_real()
 
1131
        return self._real_repository.start_write_group()
1637
1132
 
1638
1133
    def _unlock(self, token):
1639
1134
        path = self.bzrdir._path_for_remote_call(self._client)
1666
1161
            # This is just to let the _real_repository stay up to date.
1667
1162
            if self._real_repository is not None:
1668
1163
                self._real_repository.unlock()
1669
 
            elif self._write_group_tokens is not None:
1670
 
                self.abort_write_group()
1671
1164
        finally:
1672
1165
            # The rpc-level lock should be released even if there was a
1673
1166
            # problem releasing the vfs-based lock.
1685
1178
 
1686
1179
    def break_lock(self):
1687
1180
        # 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)
 
1181
        self._ensure_real()
 
1182
        return self._real_repository.break_lock()
1696
1183
 
1697
1184
    def _get_tarball(self, compression):
1698
1185
        """Return a TemporaryFile containing a repository tarball.
1716
1203
            return t
1717
1204
        raise errors.UnexpectedSmartServerResponse(response)
1718
1205
 
1719
 
    @needs_read_lock
1720
1206
    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)
 
1207
        # TODO: Option to control what format is created?
 
1208
        self._ensure_real()
 
1209
        dest_repo = self._real_repository._format.initialize(to_bzrdir,
 
1210
                                                             shared=False)
1726
1211
        dest_repo.fetch(self, revision_id=revision_id)
1727
1212
        return dest_repo
1728
1213
 
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
1214
    ### These methods are just thin shims to the VFS object for now.
1743
1215
 
1744
 
    @needs_read_lock
1745
1216
    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]
 
1217
        self._ensure_real()
 
1218
        return self._real_repository.revision_tree(revision_id)
1752
1219
 
1753
1220
    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]
 
1221
        self._ensure_real()
 
1222
        return self._real_repository.get_serializer_format()
1764
1223
 
1765
1224
    def get_commit_builder(self, branch, parents, config, timestamp=None,
1766
1225
                           timezone=None, committer=None, revprops=None,
1767
 
                           revision_id=None, lossy=False):
 
1226
                           revision_id=None):
1768
1227
        # FIXME: It ought to be possible to call this without immediately
1769
1228
        # triggering _ensure_real.  For now it's the easiest thing to do.
1770
1229
        self._ensure_real()
1771
1230
        real_repo = self._real_repository
1772
1231
        builder = real_repo.get_commit_builder(branch, parents,
1773
1232
                config, timestamp=timestamp, timezone=timezone,
1774
 
                committer=committer, revprops=revprops,
1775
 
                revision_id=revision_id, lossy=lossy)
 
1233
                committer=committer, revprops=revprops, revision_id=revision_id)
1776
1234
        return builder
1777
1235
 
1778
1236
    def add_fallback_repository(self, repository):
1786
1244
        # We need to accumulate additional repositories here, to pass them in
1787
1245
        # on various RPC's.
1788
1246
        #
1789
 
        # Make the check before we lock: this raises an exception.
1790
 
        self._check_fallback_repository(repository)
1791
1247
        if self.is_locked():
1792
1248
            # We will call fallback.unlock() when we transition to the unlocked
1793
1249
            # state, so always add a lock here. If a caller passes us a locked
1794
1250
            # repository, they are responsible for unlocking it later.
1795
1251
            repository.lock_read()
 
1252
        self._check_fallback_repository(repository)
1796
1253
        self._fallback_repositories.append(repository)
1797
1254
        # If self._real_repository was parameterised already (e.g. because a
1798
1255
        # _real_branch had its get_stacked_on_url method called), then the
1831
1288
 
1832
1289
    @needs_read_lock
1833
1290
    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
1291
        self._ensure_real()
1878
 
        return self._real_repository._iter_inventories(revision_ids, ordering)
 
1292
        return self._real_repository.get_inventory(revision_id)
1879
1293
 
1880
1294
    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()
 
1295
        self._ensure_real()
 
1296
        return self._real_repository.iter_inventories(revision_ids, ordering)
1955
1297
 
1956
1298
    @needs_read_lock
1957
1299
    def get_revision(self, revision_id):
1958
 
        return self.get_revisions([revision_id])[0]
 
1300
        self._ensure_real()
 
1301
        return self._real_repository.get_revision(revision_id)
1959
1302
 
1960
1303
    def get_transaction(self):
1961
1304
        self._ensure_real()
1963
1306
 
1964
1307
    @needs_read_lock
1965
1308
    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
 
1309
        self._ensure_real()
 
1310
        return self._real_repository.clone(a_bzrdir, revision_id=revision_id)
1970
1311
 
1971
1312
    def make_working_trees(self):
1972
1313
        """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'
 
1314
        self._ensure_real()
 
1315
        return self._real_repository.make_working_trees()
1982
1316
 
1983
1317
    def refresh_data(self):
1984
1318
        """Re-read any data needed to synchronise with disk.
2003
1337
        included_keys = result_set.intersection(result_parents)
2004
1338
        start_keys = result_set.difference(included_keys)
2005
1339
        exclude_keys = result_parents.difference(result_set)
2006
 
        result = vf_search.SearchResult(start_keys, exclude_keys,
 
1340
        result = graph.SearchResult(start_keys, exclude_keys,
2007
1341
            len(result_set), result_set)
2008
1342
        return result
2009
1343
 
2010
1344
    @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):
 
1345
    def search_missing_revision_ids(self, other, revision_id=None, find_ghosts=True):
2015
1346
        """Return the revision ids that other has that this does not.
2016
1347
 
2017
1348
        These are returned in topological order.
2018
1349
 
2019
1350
        revision_id: only return revision ids included by revision_id.
2020
1351
        """
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)
 
1352
        return repository.InterRepository.get(
 
1353
            other, self).search_missing_revision_ids(revision_id, find_ghosts)
2035
1354
 
2036
 
    def fetch(self, source, revision_id=None, find_ghosts=False,
 
1355
    def fetch(self, source, revision_id=None, pb=None, find_ghosts=False,
2037
1356
            fetch_spec=None):
2038
1357
        # No base implementation to use as RemoteRepository is not a subclass
2039
1358
        # of Repository; so this is a copy of Repository.fetch().
2050
1369
            # check that last_revision is in 'from' and then return a
2051
1370
            # no-operation.
2052
1371
            if (revision_id is not None and
2053
 
                not _mod_revision.is_null(revision_id)):
 
1372
                not revision.is_null(revision_id)):
2054
1373
                self.get_revision(revision_id)
2055
1374
            return 0, []
2056
1375
        # if there is no specific appropriate InterRepository, this will get
2057
1376
        # the InterRepository base class, which raises an
2058
1377
        # 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,
 
1378
        inter = repository.InterRepository.get(source, self)
 
1379
        return inter.fetch(revision_id=revision_id, pb=pb,
2065
1380
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
2066
1381
 
2067
1382
    def create_bundle(self, target, base, fileobj, format=None):
2069
1384
        self._real_repository.create_bundle(target, base, fileobj, format)
2070
1385
 
2071
1386
    @needs_read_lock
2072
 
    @symbol_versioning.deprecated_method(
2073
 
        symbol_versioning.deprecated_in((2, 4, 0)))
2074
1387
    def get_ancestry(self, revision_id, topo_sorted=True):
2075
1388
        self._ensure_real()
2076
1389
        return self._real_repository.get_ancestry(revision_id, topo_sorted)
2084
1397
        return self._real_repository._get_versioned_file_checker(
2085
1398
            revisions, revision_versions_cache)
2086
1399
 
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
1400
    def iter_files_bytes(self, desired_files):
2134
1401
        """See Repository.iter_file_bytes.
2135
1402
        """
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)
 
1403
        self._ensure_real()
 
1404
        return self._real_repository.iter_files_bytes(desired_files)
2165
1405
 
2166
1406
    def get_parent_map(self, revision_ids):
2167
1407
        """See bzrlib.Graph.get_parent_map()."""
2226
1466
        if parents_map is None:
2227
1467
            # Repository is not locked, so there's no cache.
2228
1468
            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)
 
1469
        # start_set is all the keys in the cache
 
1470
        start_set = set(parents_map)
 
1471
        # result set is all the references to keys in the cache
 
1472
        result_parents = set()
 
1473
        for parents in parents_map.itervalues():
 
1474
            result_parents.update(parents)
 
1475
        stop_keys = result_parents.difference(start_set)
 
1476
        # We don't need to send ghosts back to the server as a position to
 
1477
        # stop either.
 
1478
        stop_keys.difference_update(self._unstacked_provider.missing_keys)
 
1479
        key_count = len(parents_map)
 
1480
        if (NULL_REVISION in result_parents
 
1481
            and NULL_REVISION in self._unstacked_provider.missing_keys):
 
1482
            # If we pruned NULL_REVISION from the stop_keys because it's also
 
1483
            # in our cache of "missing" keys we need to increment our key count
 
1484
            # by 1, because the reconsitituted SearchResult on the server will
 
1485
            # still consider NULL_REVISION to be an included key.
 
1486
            key_count += 1
 
1487
        included_keys = start_set.intersection(result_parents)
 
1488
        start_set.difference_update(included_keys)
2238
1489
        recipe = ('manual', start_set, stop_keys, key_count)
2239
1490
        body = self._serialise_search_recipe(recipe)
2240
1491
        path = self.bzrdir._path_for_remote_call(self._client)
2289
1540
 
2290
1541
    @needs_read_lock
2291
1542
    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()
 
1543
        self._ensure_real()
 
1544
        return self._real_repository.get_signature_text(revision_id)
2310
1545
 
2311
1546
    @needs_read_lock
2312
1547
    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
1548
        self._ensure_real()
2316
1549
        return self._real_repository._get_inventory_xml(revision_id)
2317
1550
 
2318
 
    @needs_write_lock
2319
1551
    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
 
1552
        self._ensure_real()
 
1553
        return self._real_repository.reconcile(other=other, thorough=thorough)
2343
1554
 
2344
1555
    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)
 
1556
        self._ensure_real()
 
1557
        return self._real_repository.all_revision_ids()
2374
1558
 
2375
1559
    @needs_read_lock
2376
1560
    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)
 
1561
        self._ensure_real()
 
1562
        return self._real_repository.get_deltas_for_revisions(revisions,
 
1563
            specific_fileids=specific_fileids)
2411
1564
 
2412
1565
    @needs_read_lock
2413
1566
    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]
 
1567
        self._ensure_real()
 
1568
        return self._real_repository.get_revision_delta(revision_id,
 
1569
            specific_fileids=specific_fileids)
2417
1570
 
2418
1571
    @needs_read_lock
2419
1572
    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)
 
1573
        self._ensure_real()
 
1574
        return self._real_repository.revision_trees(revision_ids)
2423
1575
 
2424
1576
    @needs_read_lock
2425
1577
    def get_revision_reconcile(self, revision_id):
2433
1585
            callback_refs=callback_refs, check_repo=check_repo)
2434
1586
 
2435
1587
    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)
 
1588
        self._ensure_real()
 
1589
        return self._real_repository.copy_content_into(
 
1590
            destination, revision_id=revision_id)
2443
1591
 
2444
1592
    def _copy_repository_tarball(self, to_bzrdir, revision_id=None):
2445
1593
        # get a tarball of the remote repository, and copy from that into the
2446
1594
        # destination
 
1595
        from bzrlib import osutils
2447
1596
        import tarfile
2448
1597
        # TODO: Maybe a progress bar while streaming the tarball?
2449
 
        note(gettext("Copying repository content as tarball..."))
 
1598
        note("Copying repository content as tarball...")
2450
1599
        tar_file = self._get_tarball('bz2')
2451
1600
        if tar_file is None:
2452
1601
            return None
2457
1606
            tmpdir = osutils.mkdtemp()
2458
1607
            try:
2459
1608
                _extract_tar(tar, tmpdir)
2460
 
                tmp_bzrdir = _mod_bzrdir.BzrDir.open(tmpdir)
 
1609
                tmp_bzrdir = BzrDir.open(tmpdir)
2461
1610
                tmp_repo = tmp_bzrdir.open_repository()
2462
1611
                tmp_repo.copy_content_into(destination, revision_id)
2463
1612
            finally:
2481
1630
    @needs_write_lock
2482
1631
    def pack(self, hint=None, clean_obsolete_packs=False):
2483
1632
        """Compress the data within the repository.
 
1633
 
 
1634
        This is not currently implemented within the smart server.
2484
1635
        """
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)
 
1636
        self._ensure_real()
 
1637
        return self._real_repository.pack(hint=hint, clean_obsolete_packs=clean_obsolete_packs)
2501
1638
 
2502
1639
    @property
2503
1640
    def revisions(self):
2504
1641
        """Decorate the real repository for now.
2505
1642
 
 
1643
        In the short term this should become a real object to intercept graph
 
1644
        lookups.
 
1645
 
2506
1646
        In the long term a full blown network facility is needed.
2507
1647
        """
2508
1648
        self._ensure_real()
2536
1676
 
2537
1677
    @needs_write_lock
2538
1678
    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)
 
1679
        self._ensure_real()
 
1680
        return self._real_repository.sign_revision(revision_id, gpg_strategy)
2542
1681
 
2543
1682
    @property
2544
1683
    def texts(self):
2550
1689
        self._ensure_real()
2551
1690
        return self._real_repository.texts
2552
1691
 
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
1692
    @needs_read_lock
2580
1693
    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]
 
1694
        self._ensure_real()
 
1695
        return self._real_repository.get_revisions(revision_ids)
2616
1696
 
2617
1697
    def supports_rich_root(self):
2618
1698
        return self._format.rich_root_data
2619
1699
 
2620
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
2621
1700
    def iter_reverse_revision_history(self, revision_id):
2622
1701
        self._ensure_real()
2623
1702
        return self._real_repository.iter_reverse_revision_history(revision_id)
2626
1705
    def _serializer(self):
2627
1706
        return self._format._serializer
2628
1707
 
2629
 
    @needs_write_lock
2630
1708
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
2631
 
        signature = gpg_strategy.sign(plaintext)
2632
 
        self.add_signature_text(revision_id, signature)
 
1709
        self._ensure_real()
 
1710
        return self._real_repository.store_revision_signature(
 
1711
            gpg_strategy, plaintext, revision_id)
2633
1712
 
2634
1713
    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:]
 
1714
        self._ensure_real()
 
1715
        return self._real_repository.add_signature_text(revision_id, signature)
2650
1716
 
2651
1717
    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)
 
1718
        self._ensure_real()
 
1719
        return self._real_repository.has_signature_for_revision_id(revision_id)
2679
1720
 
2680
1721
    def item_keys_introduced_by(self, revision_ids, _files_pb=None):
2681
1722
        self._ensure_real()
2682
1723
        return self._real_repository.item_keys_introduced_by(revision_ids,
2683
1724
            _files_pb=_files_pb)
2684
1725
 
 
1726
    def revision_graph_can_have_wrong_parents(self):
 
1727
        # The answer depends on the remote repo format.
 
1728
        self._ensure_real()
 
1729
        return self._real_repository.revision_graph_can_have_wrong_parents()
 
1730
 
2685
1731
    def _find_inconsistent_revision_parents(self, revisions_iterator=None):
2686
1732
        self._ensure_real()
2687
1733
        return self._real_repository._find_inconsistent_revision_parents(
2695
1741
        providers = [self._unstacked_provider]
2696
1742
        if other is not None:
2697
1743
            providers.insert(0, other)
2698
 
        return graph.StackedParentsProvider(_LazyListJoin(
2699
 
            providers, self._fallback_repositories))
 
1744
        providers.extend(r._make_parents_provider() for r in
 
1745
                         self._fallback_repositories)
 
1746
        return graph.StackedParentsProvider(providers)
2700
1747
 
2701
1748
    def _serialise_search_recipe(self, recipe):
2702
1749
        """Serialise a graph search recipe.
2710
1757
        return '\n'.join((start_keys, stop_keys, count))
2711
1758
 
2712
1759
    def _serialise_search_result(self, search_result):
2713
 
        parts = search_result.get_network_struct()
 
1760
        if isinstance(search_result, graph.PendingAncestryResult):
 
1761
            parts = ['ancestry-of']
 
1762
            parts.extend(search_result.heads)
 
1763
        else:
 
1764
            recipe = search_result.get_recipe()
 
1765
            parts = [recipe[0], self._serialise_search_recipe(recipe)]
2714
1766
        return '\n'.join(parts)
2715
1767
 
2716
1768
    def autopack(self):
2726
1778
            raise errors.UnexpectedSmartServerResponse(response)
2727
1779
 
2728
1780
 
2729
 
class RemoteStreamSink(vf_repository.StreamSink):
 
1781
class RemoteStreamSink(repository.StreamSink):
2730
1782
 
2731
1783
    def _insert_real(self, stream, src_format, resume_tokens):
2732
1784
        self.target_repo._ensure_real()
2833
1885
        self._last_substream and self._last_stream so that the stream can be
2834
1886
        resumed by _resume_stream_with_vfs.
2835
1887
        """
2836
 
 
 
1888
                    
2837
1889
        stream_iter = iter(stream)
2838
1890
        for substream_kind, substream in stream_iter:
2839
1891
            if substream_kind == 'inventory-deltas':
2842
1894
                return
2843
1895
            else:
2844
1896
                yield substream_kind, substream
2845
 
 
2846
 
 
2847
 
class RemoteStreamSource(vf_repository.StreamSource):
 
1897
            
 
1898
 
 
1899
class RemoteStreamSource(repository.StreamSource):
2848
1900
    """Stream data from a remote server."""
2849
1901
 
2850
1902
    def get_stream(self, search):
2871
1923
 
2872
1924
    def _real_stream(self, repo, search):
2873
1925
        """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
 
1926
        
 
1927
        This never called RemoteStreamSource.get_stream, and is a heler
 
1928
        for RemoteStreamSource._get_stream to allow getting a stream 
2877
1929
        reliably whether fallback back because of old servers or trying
2878
1930
        to stream from a non-RemoteRepository (which the stacked support
2879
1931
        code will do).
2910
1962
        candidate_verbs = [
2911
1963
            ('Repository.get_stream_1.19', (1, 19)),
2912
1964
            ('Repository.get_stream', (1, 13))]
2913
 
 
2914
1965
        found_verb = False
2915
1966
        for verb, version in candidate_verbs:
2916
1967
            if medium._is_remote_before(version):
2920
1971
                    verb, args, search_bytes)
2921
1972
            except errors.UnknownSmartMethod:
2922
1973
                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
1974
            else:
2935
1975
                response_tuple, response_handler = response
2936
1976
                found_verb = True
3019
2059
 
3020
2060
    def _ensure_real(self):
3021
2061
        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)
 
2062
            self._custom_format = branch.network_format_registry.get(
 
2063
                self._network_name)
3028
2064
 
3029
2065
    def get_format_description(self):
3030
2066
        self._ensure_real()
3037
2073
        return a_bzrdir.open_branch(name=name, 
3038
2074
            ignore_fallbacks=ignore_fallbacks)
3039
2075
 
3040
 
    def _vfs_initialize(self, a_bzrdir, name, append_revisions_only):
 
2076
    def _vfs_initialize(self, a_bzrdir, name):
3041
2077
        # Initialisation when using a local bzrdir object, or a non-vfs init
3042
2078
        # method is not available on the server.
3043
2079
        # self._custom_format is always set - the start of initialize ensures
3045
2081
        if isinstance(a_bzrdir, RemoteBzrDir):
3046
2082
            a_bzrdir._ensure_real()
3047
2083
            result = self._custom_format.initialize(a_bzrdir._real_bzrdir,
3048
 
                name, append_revisions_only=append_revisions_only)
 
2084
                name)
3049
2085
        else:
3050
2086
            # 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)
 
2087
            result = self._custom_format.initialize(a_bzrdir, name)
3053
2088
        if (isinstance(a_bzrdir, RemoteBzrDir) and
3054
2089
            not isinstance(result, RemoteBranch)):
3055
2090
            result = RemoteBranch(a_bzrdir, a_bzrdir.find_repository(), result,
3056
2091
                                  name=name)
3057
2092
        return result
3058
2093
 
3059
 
    def initialize(self, a_bzrdir, name=None, repository=None,
3060
 
                   append_revisions_only=None):
 
2094
    def initialize(self, a_bzrdir, name=None):
3061
2095
        # 1) get the network name to use.
3062
2096
        if self._custom_format:
3063
2097
            network_name = self._custom_format.network_name()
3064
2098
        else:
3065
2099
            # Select the current bzrlib default and ask for that.
3066
 
            reference_bzrdir_format = _mod_bzrdir.format_registry.get('default')()
 
2100
            reference_bzrdir_format = bzrdir.format_registry.get('default')()
3067
2101
            reference_format = reference_bzrdir_format.get_branch_format()
3068
2102
            self._custom_format = reference_format
3069
2103
            network_name = reference_format.network_name()
3070
2104
        # Being asked to create on a non RemoteBzrDir:
3071
2105
        if not isinstance(a_bzrdir, RemoteBzrDir):
3072
 
            return self._vfs_initialize(a_bzrdir, name=name,
3073
 
                append_revisions_only=append_revisions_only)
 
2106
            return self._vfs_initialize(a_bzrdir, name=name)
3074
2107
        medium = a_bzrdir._client._medium
3075
2108
        if medium._is_remote_before((1, 13)):
3076
 
            return self._vfs_initialize(a_bzrdir, name=name,
3077
 
                append_revisions_only=append_revisions_only)
 
2109
            return self._vfs_initialize(a_bzrdir, name=name)
3078
2110
        # Creating on a remote bzr dir.
3079
2111
        # 2) try direct creation via RPC
3080
2112
        path = a_bzrdir._path_for_remote_call(a_bzrdir._client)
3087
2119
        except errors.UnknownSmartMethod:
3088
2120
            # Fallback - use vfs methods
3089
2121
            medium._remember_remote_is_before((1, 13))
3090
 
            return self._vfs_initialize(a_bzrdir, name=name,
3091
 
                    append_revisions_only=append_revisions_only)
 
2122
            return self._vfs_initialize(a_bzrdir, name=name)
3092
2123
        if response[0] != 'ok':
3093
2124
            raise errors.UnexpectedSmartServerResponse(response)
3094
2125
        # Turn the response into a RemoteRepository object.
3095
2126
        format = RemoteBranchFormat(network_name=response[1])
3096
2127
        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
 
2128
        if response[2] == '':
 
2129
            repo_bzrdir = a_bzrdir
3108
2130
        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)
 
2131
            repo_bzrdir = RemoteBzrDir(
 
2132
                a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format,
 
2133
                a_bzrdir._client)
 
2134
        remote_repo = RemoteRepository(repo_bzrdir, repo_format)
3116
2135
        remote_branch = RemoteBranch(a_bzrdir, remote_repo,
3117
2136
            format=format, setup_stacking=False, name=name)
3118
 
        if append_revisions_only:
3119
 
            remote_branch.set_append_revisions_only(append_revisions_only)
3120
2137
        # XXX: We know this is a new branch, so it must have revno 0, revid
3121
2138
        # NULL_REVISION. Creating the branch locked would make this be unable
3122
2139
        # to be wrong; here its simply very unlikely to be wrong. RBC 20090225
3141
2158
        self._ensure_real()
3142
2159
        return self._custom_format.supports_set_append_revisions_only()
3143
2160
 
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
2161
 
3220
2162
class RemoteBranch(branch.Branch, _RpcHelper, lock._RelockDebugMixin):
3221
2163
    """Branch stored on a server accessed by HPSS RPC.
3224
2166
    """
3225
2167
 
3226
2168
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
3227
 
        _client=None, format=None, setup_stacking=True, name=None,
3228
 
        possible_transports=None):
 
2169
        _client=None, format=None, setup_stacking=True, name=None):
3229
2170
        """Create a RemoteBranch instance.
3230
2171
 
3231
2172
        :param real_branch: An optional local implementation of the branch
3296
2237
            hook(self)
3297
2238
        self._is_stacked = False
3298
2239
        if setup_stacking:
3299
 
            self._setup_stacking(possible_transports)
 
2240
            self._setup_stacking()
3300
2241
 
3301
 
    def _setup_stacking(self, possible_transports):
 
2242
    def _setup_stacking(self):
3302
2243
        # configure stacking into the remote repository, by reading it from
3303
2244
        # the vfs branch.
3304
2245
        try:
3307
2248
            errors.UnstackableRepositoryFormat), e:
3308
2249
            return
3309
2250
        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)
 
2251
        self._activate_fallback_location(fallback_url)
3317
2252
 
3318
2253
    def _get_config(self):
3319
2254
        return RemoteBranchConfig(self)
3320
2255
 
3321
 
    def _get_config_store(self):
3322
 
        return RemoteBranchStore(self)
3323
 
 
3324
2256
    def _get_real_transport(self):
3325
2257
        # if we try vfs access, return the real branch's vfs transport
3326
2258
        self._ensure_real()
3389
2321
                self.bzrdir, self._client)
3390
2322
        return self._control_files
3391
2323
 
 
2324
    def _get_checkout_format(self):
 
2325
        self._ensure_real()
 
2326
        return self._real_branch._get_checkout_format()
 
2327
 
3392
2328
    def get_physical_lock_status(self):
3393
2329
        """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')
 
2330
        # should be an API call to the server, as branches must be lockable.
 
2331
        self._ensure_real()
 
2332
        return self._real_branch.get_physical_lock_status()
3403
2333
 
3404
2334
    def get_stacked_on_url(self):
3405
2335
        """Get the URL this branch is stacked against.
3432
2362
            self._is_stacked = False
3433
2363
        else:
3434
2364
            self._is_stacked = True
3435
 
 
 
2365
        
3436
2366
    def _vfs_get_tags_bytes(self):
3437
2367
        self._ensure_real()
3438
2368
        return self._real_branch._get_tags_bytes()
3439
2369
 
3440
 
    @needs_read_lock
3441
2370
    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
2371
        medium = self._client._medium
3448
2372
        if medium._is_remote_before((1, 13)):
3449
2373
            return self._vfs_get_tags_bytes()
3459
2383
        return self._real_branch._set_tags_bytes(bytes)
3460
2384
 
3461
2385
    def _set_tags_bytes(self, bytes):
3462
 
        if self.is_locked():
3463
 
            self._tags_bytes = bytes
3464
2386
        medium = self._client._medium
3465
2387
        if medium._is_remote_before((1, 18)):
3466
2388
            self._vfs_set_tags_bytes(bytes)
3498
2420
            repo_token = self.repository.lock_write().repository_token
3499
2421
            self.repository.unlock()
3500
2422
        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])
 
2423
        response = self._call(
 
2424
            'Branch.lock_write', self._remote_path(), branch_token,
 
2425
            repo_token or '', **err_context)
3511
2426
        if response[0] != 'ok':
3512
2427
            raise errors.UnexpectedSmartServerResponse(response)
3513
2428
        ok, branch_token, repo_token = response
3589
2504
            self.repository.unlock()
3590
2505
 
3591
2506
    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)
 
2507
        self._ensure_real()
 
2508
        return self._real_branch.break_lock()
3600
2509
 
3601
2510
    def leave_lock_in_place(self):
3602
2511
        if not self._lock_token:
3626
2535
            missing_parent = parent_map[missing_parent]
3627
2536
        raise errors.RevisionNotPresent(missing_parent, self.repository)
3628
2537
 
3629
 
    def _read_last_revision_info(self):
 
2538
    def _last_revision_info(self):
3630
2539
        response = self._call('Branch.last_revision_info', self._remote_path())
3631
2540
        if response[0] != 'ok':
3632
2541
            raise SmartProtocolError('unexpected response code %s' % (response,))
3695
2604
            raise errors.UnexpectedSmartServerResponse(response)
3696
2605
        self._run_post_change_branch_tip_hooks(old_revno, old_revid)
3697
2606
 
3698
 
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
3699
2607
    @needs_write_lock
3700
2608
    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
2609
        # Send just the tip revision of the history; the server will generate
3707
2610
        # the full history from that.  If the revision doesn't exist in this
3708
2611
        # branch, NoSuchRevision will be raised.
3766
2669
            _override_hook_target=self, **kwargs)
3767
2670
 
3768
2671
    @needs_read_lock
3769
 
    def push(self, target, overwrite=False, stop_revision=None, lossy=False):
 
2672
    def push(self, target, overwrite=False, stop_revision=None):
3770
2673
        self._ensure_real()
3771
2674
        return self._real_branch.push(
3772
 
            target, overwrite=overwrite, stop_revision=stop_revision, lossy=lossy,
 
2675
            target, overwrite=overwrite, stop_revision=stop_revision,
3773
2676
            _override_hook_source_branch=self)
3774
2677
 
3775
2678
    def is_locked(self):
3776
2679
        return self._lock_count >= 1
3777
2680
 
3778
2681
    @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
2682
    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)
 
2683
        self._ensure_real()
 
2684
        return self._real_branch.revision_id_to_revno(revision_id)
3813
2685
 
3814
2686
    @needs_write_lock
3815
2687
    def set_last_revision_info(self, revno, revision_id):
3816
2688
        # XXX: These should be returned by the set_last_revision_info verb
3817
2689
        old_revno, old_revid = self.last_revision_info()
3818
2690
        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)
 
2691
        revision_id = ensure_null(revision_id)
3821
2692
        try:
3822
2693
            response = self._call('Branch.set_last_revision_info',
3823
2694
                self._remote_path(), self._lock_token, self._repo_lock_token,
3852
2723
            except errors.UnknownSmartMethod:
3853
2724
                medium._remember_remote_is_before((1, 6))
3854
2725
        self._clear_cached_state_of_remote_branch_only()
3855
 
        self._set_revision_history(self._lefthand_history(revision_id,
 
2726
        self.set_revision_history(self._lefthand_history(revision_id,
3856
2727
            last_rev=last_rev,other_branch=other_branch))
3857
2728
 
3858
2729
    def set_push_location(self, location):
3859
2730
        self._ensure_real()
3860
2731
        return self._real_branch.set_push_location(location)
3861
2732
 
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
2733
 
3890
2734
class RemoteConfig(object):
3891
2735
    """A Config that reads and writes from smart verbs.
3905
2749
        """
3906
2750
        try:
3907
2751
            configobj = self._get_configobj()
3908
 
            section_obj = None
3909
2752
            if section is None:
3910
2753
                section_obj = configobj
3911
2754
            else:
3912
2755
                try:
3913
2756
                    section_obj = configobj[section]
3914
2757
                except KeyError:
3915
 
                    pass
3916
 
            if section_obj is None:
3917
 
                value = default
3918
 
            else:
3919
 
                value = section_obj.get(name, default)
 
2758
                    return default
 
2759
            return section_obj.get(name, default)
3920
2760
        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
 
2761
            return self._vfs_get_option(name, section, default)
3925
2762
 
3926
2763
    def _response_to_configobj(self, response):
3927
2764
        if len(response[0]) and response[0][0] != 'ok':
3928
2765
            raise errors.UnexpectedSmartServerResponse(response)
3929
2766
        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
 
2767
        return config.ConfigObj(lines, encoding='utf-8')
3934
2768
 
3935
2769
 
3936
2770
class RemoteBranchConfig(RemoteConfig):
4044
2878
        return self._bzrdir._real_bzrdir
4045
2879
 
4046
2880
 
 
2881
 
4047
2882
def _extract_tar(tar, to_dir):
4048
2883
    """Extract all the contents of a tarfile object.
4049
2884
 
4053
2888
        tar.extract(tarinfo, to_dir)
4054
2889
 
4055
2890
 
4056
 
error_translators = registry.Registry()
4057
 
no_context_error_translators = registry.Registry()
4058
 
 
4059
 
 
4060
2891
def _translate_error(err, **context):
4061
2892
    """Translate an ErrorFromSmartServer into a more useful error.
4062
2893
 
4091
2922
                    'Missing key %r in context %r', key_err.args[0], context)
4092
2923
                raise err
4093
2924
 
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):
 
2925
    if err.error_verb == 'IncompatibleRepositories':
 
2926
        raise errors.IncompatibleRepositories(err.error_args[0],
 
2927
            err.error_args[1], err.error_args[2])
 
2928
    elif err.error_verb == 'NoSuchRevision':
 
2929
        raise NoSuchRevision(find('branch'), err.error_args[0])
 
2930
    elif err.error_verb == 'nosuchrevision':
 
2931
        raise NoSuchRevision(find('repository'), err.error_args[0])
 
2932
    elif err.error_verb == 'nobranch':
 
2933
        if len(err.error_args) >= 1:
 
2934
            extra = err.error_args[0]
 
2935
        else:
 
2936
            extra = None
 
2937
        raise errors.NotBranchError(path=find('bzrdir').root_transport.base,
 
2938
            detail=extra)
 
2939
    elif err.error_verb == 'norepository':
 
2940
        raise errors.NoRepositoryPresent(find('bzrdir'))
 
2941
    elif err.error_verb == 'LockContention':
 
2942
        raise errors.LockContention('(remote lock)')
 
2943
    elif err.error_verb == 'UnlockableTransport':
 
2944
        raise errors.UnlockableTransport(find('bzrdir').root_transport)
 
2945
    elif err.error_verb == 'LockFailed':
 
2946
        raise errors.LockFailed(err.error_args[0], err.error_args[1])
 
2947
    elif err.error_verb == 'TokenMismatch':
 
2948
        raise errors.TokenMismatch(find('token'), '(remote token)')
 
2949
    elif err.error_verb == 'Diverged':
 
2950
        raise errors.DivergedBranches(find('branch'), find('other_branch'))
 
2951
    elif err.error_verb == 'TipChangeRejected':
 
2952
        raise errors.TipChangeRejected(err.error_args[0].decode('utf8'))
 
2953
    elif err.error_verb == 'UnstackableBranchFormat':
 
2954
        raise errors.UnstackableBranchFormat(*err.error_args)
 
2955
    elif err.error_verb == 'UnstackableRepositoryFormat':
 
2956
        raise errors.UnstackableRepositoryFormat(*err.error_args)
 
2957
    elif err.error_verb == 'NotStacked':
 
2958
        raise errors.NotStacked(branch=find('branch'))
 
2959
    elif err.error_verb == 'PermissionDenied':
 
2960
        path = get_path()
 
2961
        if len(err.error_args) >= 2:
 
2962
            extra = err.error_args[1]
 
2963
        else:
 
2964
            extra = None
 
2965
        raise errors.PermissionDenied(path, extra=extra)
 
2966
    elif err.error_verb == 'ReadError':
 
2967
        path = get_path()
 
2968
        raise errors.ReadError(path)
 
2969
    elif err.error_verb == 'NoSuchFile':
 
2970
        path = get_path()
 
2971
        raise errors.NoSuchFile(path)
 
2972
    elif err.error_verb == 'FileExists':
 
2973
        raise errors.FileExists(err.error_args[0])
 
2974
    elif err.error_verb == 'DirectoryNotEmpty':
 
2975
        raise errors.DirectoryNotEmpty(err.error_args[0])
 
2976
    elif err.error_verb == 'ShortReadvError':
 
2977
        args = err.error_args
 
2978
        raise errors.ShortReadvError(
 
2979
            args[0], int(args[1]), int(args[2]), int(args[3]))
 
2980
    elif err.error_verb in ('UnicodeEncodeError', 'UnicodeDecodeError'):
4189
2981
        encoding = str(err.error_args[0]) # encoding must always be a string
4190
2982
        val = err.error_args[1]
4191
2983
        start = int(err.error_args[2])
4199
2991
            raise UnicodeDecodeError(encoding, val, start, end, reason)
4200
2992
        elif err.error_verb == 'UnicodeEncodeError':
4201
2993
            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
 
 
 
2994
    elif err.error_verb == 'ReadOnlyError':
 
2995
        raise errors.TransportNotPossible('readonly transport')
 
2996
    raise errors.UnknownErrorFromSmartServer(err)