/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
1
# Copyright (C) 2006, 2007 Canonical Ltd
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
1752.2.39 by Martin Pool
[broken] implement upgrade apis on remote bzrdirs
17
# TODO: At some point, handle upgrades by just passing the whole request
18
# across to run on the server.
19
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
20
from cStringIO import StringIO
2018.5.26 by Andrew Bennetts
Extract a simple SmartClient class from RemoteTransport, and a hack to avoid VFS operations when probing for a bzrdir over a smart transport.
21
from urlparse import urlparse
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
22
2018.5.25 by Andrew Bennetts
Make sure RemoteBzrDirFormat is always registered (John Arbash Meinel, Robert Collins, Andrew Bennetts).
23
from bzrlib import branch, errors, repository
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
24
from bzrlib.branch import BranchReferenceFormat
2018.5.25 by Andrew Bennetts
Make sure RemoteBzrDirFormat is always registered (John Arbash Meinel, Robert Collins, Andrew Bennetts).
25
from bzrlib.bzrdir import BzrDir, BzrDirFormat, RemoteBzrDirFormat
2018.14.2 by Andrew Bennetts
All but one repository_implementation tests for RemoteRepository passing.
26
from bzrlib.config import BranchConfig, TreeConfig
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
27
from bzrlib.decorators import needs_read_lock, needs_write_lock
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
28
from bzrlib.errors import NoSuchRevision
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
29
from bzrlib.revision import NULL_REVISION
2018.6.1 by Robert Collins
Implement a BzrDir.open_branch smart server method for opening a branch without VFS.
30
from bzrlib.smart import client, vfs
2018.5.32 by Robert Collins
Unescape urls before handing over the wire to the smart server for the probe_transport method.
31
from bzrlib.urlutils import unescape
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
32
2018.5.25 by Andrew Bennetts
Make sure RemoteBzrDirFormat is always registered (John Arbash Meinel, Robert Collins, Andrew Bennetts).
33
# Note: RemoteBzrDirFormat is in bzrdir.py
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
34
35
class RemoteBzrDir(BzrDir):
36
    """Control directory on a remote server, accessed by HPSS."""
37
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
38
    def __init__(self, transport, _client=None):
39
        """Construct a RemoteBzrDir.
40
41
        :param _client: Private parameter for testing. Disables probing and the
42
            use of a real bzrdir.
43
        """
1752.2.39 by Martin Pool
[broken] implement upgrade apis on remote bzrdirs
44
        BzrDir.__init__(self, transport, RemoteBzrDirFormat())
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
45
        # this object holds a delegated bzrdir that uses file-level operations
46
        # to talk to the other side
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
47
        # XXX: We should go into find_format, but not allow it to find
48
        # RemoteBzrDirFormat and make sure it finds the real underlying format.
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
49
        self._real_bzrdir = None
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
50
51
        if _client is None:
52
            self._medium = transport.get_smart_client()
53
            self._client = client.SmartClient(self._medium)
54
        else:
55
            self._client = _client
56
            self._medium = None
57
            return
58
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
59
        self._ensure_real()
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
60
        path = self._path_for_remote_call(self._client)
2018.5.26 by Andrew Bennetts
Extract a simple SmartClient class from RemoteTransport, and a hack to avoid VFS operations when probing for a bzrdir over a smart transport.
61
        #self._real_bzrdir._format.probe_transport(transport)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
62
        response = self._client.call('probe_dont_use', path)
2018.5.26 by Andrew Bennetts
Extract a simple SmartClient class from RemoteTransport, and a hack to avoid VFS operations when probing for a bzrdir over a smart transport.
63
        if response == ('no',):
64
            raise errors.NotBranchError(path=transport.base)
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
65
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
66
    def _ensure_real(self):
67
        """Ensure that there is a _real_bzrdir set.
68
69
        used before calls to self._real_bzrdir.
70
        """
71
        if not self._real_bzrdir:
72
            default_format = BzrDirFormat.get_default_format()
73
            self._real_bzrdir = default_format.open(self.root_transport,
74
                _found=True)
75
1752.2.39 by Martin Pool
[broken] implement upgrade apis on remote bzrdirs
76
    def create_repository(self, shared=False):
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
77
        return RemoteRepository(
78
            self, self._real_bzrdir.create_repository(shared=shared))
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
79
80
    def create_branch(self):
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
81
        real_branch = self._real_bzrdir.create_branch()
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
82
        return RemoteBranch(self, self.find_repository(), real_branch)
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
83
84
    def create_workingtree(self, revision_id=None):
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
85
        real_workingtree = self._real_bzrdir.create_workingtree(revision_id=revision_id)
86
        return RemoteWorkingTree(self, real_workingtree)
1752.2.39 by Martin Pool
[broken] implement upgrade apis on remote bzrdirs
87
2018.6.1 by Robert Collins
Implement a BzrDir.open_branch smart server method for opening a branch without VFS.
88
    def open_branch(self, _unsupported=False):
89
        assert _unsupported == False, 'unsupported flag support not implemented yet.'
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
90
        path = self._path_for_remote_call(self._client)
91
        response = self._client.call('BzrDir.open_branch', path)
92
        if response[0] == 'ok':
93
            if response[1] == '':
94
                # branch at this location.
95
                return RemoteBranch(self, self.find_repository())
96
            else:
97
                # a branch reference, use the existing BranchReference logic.
98
                format = BranchReferenceFormat()
99
                return format.open(self, _found=True, location=response[1])
100
        elif response == ('nobranch',):
2018.6.1 by Robert Collins
Implement a BzrDir.open_branch smart server method for opening a branch without VFS.
101
            raise errors.NotBranchError(path=self.root_transport.base)
102
        else:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
103
            assert False, 'unexpected response code %r' % (response,)
104
                
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
105
    def open_repository(self):
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
106
        path = self._path_for_remote_call(self._client)
107
        response = self._client.call('BzrDir.find_repository', path)
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
108
        assert response[0] in ('ok', 'norepository'), \
2018.5.52 by Wouter van Heyst
Provide more information when encountering unexpected responses from a smart
109
            'unexpected response code %s' % (response,)
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
110
        if response[0] == 'norepository':
111
            raise errors.NoRepositoryPresent(self)
2018.5.34 by Robert Collins
Get test_remote.BasicRemoteObjectTests.test_open_remote_branch passing by implementing a remote method BzrDir.find_repository.
112
        if response[1] == '':
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
113
            return RemoteRepository(self)
2018.5.34 by Robert Collins
Get test_remote.BasicRemoteObjectTests.test_open_remote_branch passing by implementing a remote method BzrDir.find_repository.
114
        else:
115
            raise errors.NoRepositoryPresent(self)
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
116
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
117
    def open_workingtree(self):
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
118
        return RemoteWorkingTree(self, self._real_bzrdir.open_workingtree())
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
119
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
120
    def _path_for_remote_call(self, client):
2018.6.1 by Robert Collins
Implement a BzrDir.open_branch smart server method for opening a branch without VFS.
121
        """Return the path to be used for this bzrdir in a remote call."""
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
122
        return client.remote_path_from_transport(self.root_transport)
2018.6.1 by Robert Collins
Implement a BzrDir.open_branch smart server method for opening a branch without VFS.
123
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
124
    def get_branch_transport(self, branch_format):
125
        return self._real_bzrdir.get_branch_transport(branch_format)
126
1752.2.43 by Andrew Bennetts
Fix get_{branch,repository,workingtree}_transport.
127
    def get_repository_transport(self, repository_format):
128
        return self._real_bzrdir.get_repository_transport(repository_format)
129
130
    def get_workingtree_transport(self, workingtree_format):
131
        return self._real_bzrdir.get_workingtree_transport(workingtree_format)
132
1752.2.39 by Martin Pool
[broken] implement upgrade apis on remote bzrdirs
133
    def can_convert_format(self):
134
        """Upgrading of remote bzrdirs is not supported yet."""
135
        return False
136
137
    def needs_format_conversion(self, format=None):
138
        """Upgrading of remote bzrdirs is not supported yet."""
139
        return False
140
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
141
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
142
class RemoteRepositoryFormat(repository.RepositoryFormat):
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
143
    """Format for repositories accessed over rpc.
144
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
145
    Instances of this repository are represented by RemoteRepository
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
146
    instances.
147
    """
148
149
    _matchingbzrdir = RemoteBzrDirFormat
150
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
151
    def initialize(self, a_bzrdir, shared=False):
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
152
        assert isinstance(a_bzrdir, RemoteBzrDir)
153
        return a_bzrdir.create_repository(shared=shared)
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
154
    
155
    def open(self, a_bzrdir):
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
156
        assert isinstance(a_bzrdir, RemoteBzrDir)
157
        return a_bzrdir.open_repository()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
158
159
    def get_format_description(self):
160
        return 'bzr remote repository'
161
162
    def __eq__(self, other):
1752.2.87 by Andrew Bennetts
Make tests pass.
163
        return self.__class__ == other.__class__
164
165
    rich_root_data = False
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
166
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
167
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
168
class RemoteRepository(object):
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
169
    """Repository accessed over rpc.
170
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
171
    For the moment everything is delegated to IO-like operations over
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
172
    the transport.
173
    """
174
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
175
    def __init__(self, remote_bzrdir, real_repository=None, _client=None):
2018.5.34 by Robert Collins
Get test_remote.BasicRemoteObjectTests.test_open_remote_branch passing by implementing a remote method BzrDir.find_repository.
176
        """Create a RemoteRepository instance.
177
        
178
        :param remote_bzrdir: The bzrdir hosting this repository.
179
        :param real_repository: If not None, a local implementation of the
180
            repository logic for the repository, usually accessing the data
181
            via the VFS.
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
182
        :param _client: Private testing parameter - override the smart client
183
            to be used by the repository.
2018.5.34 by Robert Collins
Get test_remote.BasicRemoteObjectTests.test_open_remote_branch passing by implementing a remote method BzrDir.find_repository.
184
        """
185
        if real_repository:
2018.5.36 by Andrew Bennetts
Fix typo, and clean up some ununsed import warnings from pyflakes at the same time.
186
            self._real_repository = real_repository
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
187
        else:
188
            self._real_repository = None
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
189
        self.bzrdir = remote_bzrdir
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
190
        if _client is None:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
191
            self._client = client.SmartClient(self.bzrdir._medium)
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
192
        else:
193
            self._client = _client
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
194
        self._format = RemoteRepositoryFormat()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
195
        self._lock_mode = None
196
        self._lock_token = None
197
        self._lock_count = 0
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
198
        self._leave_lock = False
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
199
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
200
    def _ensure_real(self):
201
        """Ensure that there is a _real_repository set.
202
203
        used before calls to self._real_repository.
204
        """
205
        if not self._real_repository:
206
            self.bzrdir._ensure_real()
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
207
            #self._real_repository = self.bzrdir._real_bzrdir.open_repository()
208
            self._set_real_repository(self.bzrdir._real_bzrdir.open_repository())
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
209
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
210
    def get_revision_graph(self, revision_id=None):
211
        """See Repository.get_revision_graph()."""
212
        if revision_id is None:
213
            revision_id = ''
214
        elif revision_id == NULL_REVISION:
215
            return {}
216
217
        path = self.bzrdir._path_for_remote_call(self._client)
218
        response = self._client.call2('Repository.get_revision_graph', path, revision_id.encode('utf8'))
219
        assert response[0][0] in ('ok', 'nosuchrevision'), 'unexpected response code %s' % (response[0],)
220
        if response[0][0] == 'ok':
221
            coded = response[1].read_body_bytes()
222
            lines = coded.decode('utf8').split('\n')
223
            revision_graph = {}
224
            # FIXME
225
            for line in lines:
226
                d = list(line.split())
227
                revision_graph[d[0]] = d[1:]
228
                
229
            return revision_graph
230
        else:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
231
            response_body = response[1].read_body_bytes()
232
            assert response_body == ''
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
233
            raise NoSuchRevision(self, revision_id)
234
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
235
    def has_revision(self, revision_id):
236
        """See Repository.has_revision()."""
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
237
        if revision_id is None:
238
            # The null revision is always present.
239
            return True
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
240
        path = self.bzrdir._path_for_remote_call(self._client)
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
241
        response = self._client.call('Repository.has_revision', path, revision_id.encode('utf8'))
2018.5.52 by Wouter van Heyst
Provide more information when encountering unexpected responses from a smart
242
        assert response[0] in ('ok', 'no'), 'unexpected response code %s' % (response,)
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
243
        return response[0] == 'ok'
244
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
245
    def gather_stats(self, revid=None, committers=None):
2018.5.62 by Robert Collins
Stub out RemoteRepository.gather_stats while its implemented in parallel.
246
        """See Repository.gather_stats()."""
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
247
        path = self.bzrdir._path_for_remote_call(self._client)
248
        if revid in (None, NULL_REVISION):
249
            fmt_revid = ''
250
        else:
251
            fmt_revid = revid.encode('utf8')
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
252
        if committers is None or not committers:
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
253
            fmt_committers = 'no'
254
        else:
255
            fmt_committers = 'yes'
256
        response = self._client.call2('Repository.gather_stats', path,
257
                                      fmt_revid, fmt_committers)
258
        assert response[0][0] == 'ok', \
259
            'unexpected response code %s' % (response[0],)
260
261
        body = response[1].read_body_bytes()
262
        result = {}
263
        for line in body.split('\n'):
264
            if not line:
265
                continue
266
            key, val_text = line.split(':')
267
            if key in ('revisions', 'size', 'committers'):
268
                result[key] = int(val_text)
269
            elif key in ('firstrev', 'latestrev'):
270
                values = val_text.split(' ')[1:]
271
                result[key] = (float(values[0]), long(values[1]))
272
273
        return result
2018.5.62 by Robert Collins
Stub out RemoteRepository.gather_stats while its implemented in parallel.
274
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
275
    def get_physical_lock_status(self):
276
        """See Repository.get_physical_lock_status()."""
277
        return False
278
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
279
    def is_shared(self):
280
        """See Repository.is_shared()."""
281
        path = self.bzrdir._path_for_remote_call(self._client)
282
        response = self._client.call('Repository.is_shared', path)
283
        assert response[0] in ('yes', 'no'), 'unexpected response code %s' % (response,)
284
        return response[0] == 'yes'
285
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
286
    def lock_read(self):
287
        # wrong eventually - want a local lock cache context
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
288
        if not self._lock_mode:
289
            self._lock_mode = 'r'
290
            self._lock_count = 1
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
291
            if self._real_repository is not None:
292
                self._real_repository.lock_read()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
293
        else:
294
            self._lock_count += 1
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
295
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
296
    def _remote_lock_write(self, token):
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
297
        path = self.bzrdir._path_for_remote_call(self._client)
298
        if token is None:
299
            token = ''
300
        response = self._client.call('Repository.lock_write', path, token)
301
        if response[0] == 'ok':
302
            ok, token = response
303
            return token
304
        elif response[0] == 'LockContention':
305
            raise errors.LockContention('(remote lock)')
306
        else:
307
            assert False, 'unexpected response code %s' % (response,)
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
308
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
309
    def lock_write(self, token=None):
310
        if not self._lock_mode:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
311
            self._lock_token = self._remote_lock_write(token)
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
312
            assert self._lock_token, 'Remote server did not return a token!'
313
            if self._real_repository is not None:
314
                self._real_repository.lock_write(token=self._lock_token)
315
            if token is not None:
316
                self._leave_lock = True
317
            else:
318
                self._leave_lock = False
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
319
            self._lock_mode = 'w'
320
            self._lock_count = 1
321
        elif self._lock_mode == 'r':
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
322
            raise errors.ReadOnlyError(self)
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
323
        else:
324
            self._lock_count += 1
325
        return self._lock_token
326
327
    def leave_lock_in_place(self):
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
328
        self._leave_lock = True
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
329
330
    def dont_leave_lock_in_place(self):
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
331
        self._leave_lock = False
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
332
333
    def _set_real_repository(self, repository):
334
        """Set the _real_repository for this repository.
335
336
        :param repository: The repository to fallback to for non-hpss
337
            implemented operations.
338
        """
339
        self._real_repository = repository
340
        if self._lock_mode == 'w':
341
            # if we are already locked, the real repository must be able to
342
            # acquire the lock with our token.
343
            self._real_repository.lock_write(self._lock_token)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
344
        elif self._lock_mode == 'r':
345
            self._real_repository.lock_read()
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
346
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
347
    def _unlock(self, token):
348
        path = self.bzrdir._path_for_remote_call(self._client)
349
        response = self._client.call('Repository.unlock', path, token)
350
        if response == ('ok',):
351
            return
352
        elif response[0] == 'TokenMismatch':
353
            raise errors.TokenMismatch(token, '(remote token)')
354
        else:
355
            assert False, 'unexpected response code %s' % (response,)
356
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
357
    def unlock(self):
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
358
        self._lock_count -= 1
359
        if not self._lock_count:
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
360
            mode = self._lock_mode
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
361
            self._lock_mode = None
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
362
            if self._real_repository is not None:
363
                self._real_repository.unlock()
364
            if mode != 'w':
365
                return
366
            assert self._lock_token, 'Locked, but no token!'
367
            token = self._lock_token
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
368
            self._lock_token = None
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
369
            if not self._leave_lock:
370
                self._unlock(token)
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
371
372
    def break_lock(self):
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
373
        # should hand off to the network
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
374
        self._ensure_real()
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
375
        return self._real_repository.break_lock()
376
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
377
    ### These methods are just thin shims to the VFS object for now.
378
379
    def revision_tree(self, revision_id):
380
        self._ensure_real()
381
        return self._real_repository.revision_tree(revision_id)
382
383
    def get_commit_builder(self, branch, parents, config, timestamp=None,
384
                           timezone=None, committer=None, revprops=None,
385
                           revision_id=None):
386
        # FIXME: It ought to be possible to call this without immediately
387
        # triggering _ensure_real.  For now it's the easiest thing to do.
388
        self._ensure_real()
389
        builder = self._real_repository.get_commit_builder(branch, parents,
390
                config, timestamp=timestamp, timezone=timezone,
391
                committer=committer, revprops=revprops, revision_id=revision_id)
392
        # Make the builder use this RemoteRepository rather than the real one.
393
        builder.repository = self
394
        return builder
395
396
    @needs_write_lock
397
    def add_inventory(self, revid, inv, parents):
398
        self._ensure_real()
399
        return self._real_repository.add_inventory(revid, inv, parents)
400
401
    @needs_write_lock
402
    def add_revision(self, rev_id, rev, inv=None, config=None):
403
        self._ensure_real()
404
        return self._real_repository.add_revision(
405
            rev_id, rev, inv=inv, config=config)
406
407
    @needs_read_lock
408
    def get_inventory(self, revision_id):
409
        self._ensure_real()
410
        return self._real_repository.get_inventory(revision_id)
411
412
    @needs_read_lock
413
    def get_revision(self, revision_id):
414
        self._ensure_real()
415
        return self._real_repository.get_revision(revision_id)
416
417
    @property
418
    def weave_store(self):
419
        self._ensure_real()
420
        return self._real_repository.weave_store
421
422
    def get_transaction(self):
423
        self._ensure_real()
424
        return self._real_repository.get_transaction()
425
426
    @needs_read_lock
427
    def clone(self, a_bzrdir, revision_id=None, basis=None):
428
        self._ensure_real()
429
        return self._real_repository.clone(
430
            a_bzrdir, revision_id=revision_id, basis=basis)
431
432
    def make_working_trees(self):
433
        return False
434
435
    def fetch(self, source, revision_id=None, pb=None):
436
        self._ensure_real()
437
        return self._real_repository.fetch(
438
            source, revision_id=revision_id, pb=pb)
439
440
    @property
441
    def control_weaves(self):
442
        self._ensure_real()
443
        return self._real_repository.control_weaves
444
445
    @needs_read_lock
446
    def get_ancestry(self, revision_id):
447
        self._ensure_real()
448
        return self._real_repository.get_ancestry(revision_id)
449
450
    @needs_read_lock
451
    def get_inventory_weave(self):
452
        self._ensure_real()
453
        return self._real_repository.get_inventory_weave()
454
455
    def fileids_altered_by_revision_ids(self, revision_ids):
456
        self._ensure_real()
457
        return self._real_repository.fileids_altered_by_revision_ids(revision_ids)
458
459
    @needs_read_lock
460
    def get_signature_text(self, revision_id):
461
        self._ensure_real()
462
        return self._real_repository.get_signature_text(revision_id)
463
464
    @needs_read_lock
465
    def get_revision_graph_with_ghosts(self, revision_ids=None):
466
        self._ensure_real()
467
        return self._real_repository.get_revision_graph_with_ghosts(
468
            revision_ids=revision_ids)
469
470
    @needs_read_lock
471
    def get_inventory_xml(self, revision_id):
472
        self._ensure_real()
473
        return self._real_repository.get_inventory_xml(revision_id)
474
475
    def deserialise_inventory(self, revision_id, xml):
476
        self._ensure_real()
477
        return self._real_repository.deserialise_inventory(revision_id, xml)
478
479
    def reconcile(self, other=None, thorough=False):
480
        self._ensure_real()
481
        return self._real_repository.reconcile(other=other, thorough=thorough)
482
        
483
    def all_revision_ids(self):
484
        self._ensure_real()
485
        return self._real_repository.all_revision_ids()
486
    
487
    @needs_read_lock
488
    def get_deltas_for_revisions(self, revisions):
489
        self._ensure_real()
490
        return self._real_repository.get_deltas_for_revisions(revisions)
491
492
    @needs_read_lock
493
    def get_revision_delta(self, revision_id):
494
        self._ensure_real()
495
        return self._real_repository.get_revision_delta(revision_id)
496
497
    @needs_read_lock
498
    def revision_trees(self, revision_ids):
499
        self._ensure_real()
500
        return self._real_repository.revision_trees(revision_ids)
501
502
    @needs_read_lock
503
    def get_revision_reconcile(self, revision_id):
504
        self._ensure_real()
505
        return self._real_repository.get_revision_reconcile(revision_id)
506
507
    @needs_read_lock
508
    def check(self, revision_ids):
509
        self._ensure_real()
510
        return self._real_repository.check(revision_ids)
511
512
    def copy_content_into(self, destination, revision_id=None, basis=None):
513
        self._ensure_real()
514
        return self._real_repository.copy_content_into(
515
            destination, revision_id=revision_id, basis=basis)
516
517
    def set_make_working_trees(self, new_value):
518
        raise NotImplementedError(self.set_make_working_trees)
519
520
    @needs_write_lock
521
    def sign_revision(self, revision_id, gpg_strategy):
522
        self._ensure_real()
523
        return self._real_repository.sign_revision(revision_id, gpg_strategy)
524
525
    @needs_read_lock
526
    def get_revisions(self, revision_ids):
527
        self._ensure_real()
528
        return self._real_repository.get_revisions(revision_ids)
529
530
    def supports_rich_root(self):
531
        # Perhaps this should return True depending on the real repository, but
532
        # for now we just take the easy option and assume we can't handle rich
533
        # root data.
534
        return False
535
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
536
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
537
class RemoteBranchLockableFiles(object):
538
    """A 'LockableFiles' implementation that talks to a smart server.
539
    
540
    This is not a public interface class.
541
    """
542
543
    def __init__(self, bzrdir, _client):
544
        self.bzrdir = bzrdir
545
        self._client = _client
546
547
    def get(self, path):
548
        """'get' a remote path as per the LockableFiles interface.
549
550
        :param path: the file to 'get'. If this is 'branch.conf', we do not
551
             just retrieve a file, instead we ask the smart server to generate
552
             a configuration for us - which is retrieved as an INI file.
553
        """
554
        assert path == 'branch.conf'
555
        path = self.bzrdir._path_for_remote_call(self._client)
556
        response = self._client.call2('Branch.get_config_file', path)
2018.10.1 by v.ladeuil+lp at free
Line too long
557
        assert response[0][0] == 'ok', \
558
            'unexpected response code %s' % (response[0],)
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
559
        return StringIO(response[1].read_body_bytes())
560
561
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
562
class RemoteBranchFormat(branch.BranchFormat):
563
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
564
    def get_format_description(self):
565
        return 'Remote BZR Branch'
566
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
567
    def get_format_string(self):
568
        return 'Remote BZR Branch'
569
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
570
    def open(self, a_bzrdir):
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
571
        assert isinstance(a_bzrdir, RemoteBzrDir)
572
        return a_bzrdir.open_branch()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
573
574
    def initialize(self, a_bzrdir):
575
        assert isinstance(a_bzrdir, RemoteBzrDir)
576
        return a_bzrdir.create_branch()
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
577
578
579
class RemoteBranch(branch.Branch):
580
    """Branch stored on a server accessed by HPSS RPC.
581
582
    At the moment most operations are mapped down to simple file operations.
583
    """
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
584
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
585
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
586
        _client=None):
2018.5.34 by Robert Collins
Get test_remote.BasicRemoteObjectTests.test_open_remote_branch passing by implementing a remote method BzrDir.find_repository.
587
        """Create a RemoteBranch instance.
588
589
        :param real_branch: An optional local implementation of the branch
590
            format, usually accessing the data via the VFS.
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
591
        :param _client: Private parameter for testing.
2018.5.34 by Robert Collins
Get test_remote.BasicRemoteObjectTests.test_open_remote_branch passing by implementing a remote method BzrDir.find_repository.
592
        """
1752.2.64 by Andrew Bennetts
Improve how RemoteBzrDir.open_branch works to handle references and not double-open repositories.
593
        self.bzrdir = remote_bzrdir
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
594
        if _client is not None:
595
            self._client = _client
596
        else:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
597
            self._client = client.SmartClient(self.bzrdir._medium)
1752.2.64 by Andrew Bennetts
Improve how RemoteBzrDir.open_branch works to handle references and not double-open repositories.
598
        self.repository = remote_repository
2018.5.34 by Robert Collins
Get test_remote.BasicRemoteObjectTests.test_open_remote_branch passing by implementing a remote method BzrDir.find_repository.
599
        if real_branch is not None:
600
            self._real_branch = real_branch
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
601
            # Give the remote repository the matching real repo.
602
            self.repository._set_real_repository(self._real_branch.repository)
603
            # Give the branch the remote repository to let fast-pathing happen.
604
            self._real_branch.repository = self.repository
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
605
        else:
606
            self._real_branch = None
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
607
        # Fill out expected attributes of branch for bzrlib api users.
1752.2.64 by Andrew Bennetts
Improve how RemoteBzrDir.open_branch works to handle references and not double-open repositories.
608
        self._format = RemoteBranchFormat()
2018.5.55 by Robert Collins
Give RemoteBranch a base url in line with the Branch protocol.
609
        self.base = self.bzrdir.root_transport.base
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
610
        self.control_files = RemoteBranchLockableFiles(self.bzrdir, self._client)
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
611
        self._lock_mode = None
612
        self._lock_token = None
613
        self._lock_count = 0
614
        self._leave_lock = False
1752.2.64 by Andrew Bennetts
Improve how RemoteBzrDir.open_branch works to handle references and not double-open repositories.
615
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
616
    def _ensure_real(self):
617
        """Ensure that there is a _real_branch set.
618
619
        used before calls to self._real_branch.
620
        """
621
        if not self._real_branch:
622
            assert vfs.vfs_enabled()
623
            self.bzrdir._ensure_real()
624
            self._real_branch = self.bzrdir._real_bzrdir.open_branch()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
625
            # Give the remote repository the matching real repo.
626
            self.repository._set_real_repository(self._real_branch.repository)
627
            # Give the branch the remote repository to let fast-pathing happen.
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
628
            self._real_branch.repository = self.repository
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
629
            # XXX: deal with _lock_mode == 'w'
630
            if self._lock_mode == 'r':
631
                self._real_branch.lock_read()
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
632
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
633
    def get_physical_lock_status(self):
634
        """See Branch.get_physical_lock_status()."""
635
        # should be an API call to the server, as branches must be lockable.
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
636
        self._ensure_real()
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
637
        return self._real_branch.get_physical_lock_status()
638
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
639
    def lock_read(self):
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
640
        if not self._lock_mode:
641
            self._lock_mode = 'r'
642
            self._lock_count = 1
643
            if self._real_branch is not None:
644
                self._real_branch.lock_read()
645
        else:
646
            self._lock_count += 1
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
647
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
648
    def _remote_lock_write(self, tokens):
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
649
        if tokens is None:
650
            branch_token = repo_token = ''
651
        else:
652
            branch_token, repo_token = tokens
653
        path = self.bzrdir._path_for_remote_call(self._client)
654
        response = self._client.call('Branch.lock_write', path, branch_token,
655
                                     repo_token)
656
        if response[0] == 'ok':
657
            ok, branch_token, repo_token = response
658
            return branch_token, repo_token
659
        elif response[0] == 'LockContention':
660
            raise errors.LockContention('(remote lock)')
661
        elif response[0] == 'TokenMismatch':
662
            raise errors.TokenMismatch(tokens, '(remote tokens)')
663
        else:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
664
            assert False, 'unexpected response code %r' % (response,)
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
665
            
666
    def lock_write(self, tokens=None):
667
        if not self._lock_mode:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
668
            remote_tokens = self._remote_lock_write(tokens)
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
669
            self._lock_token, self._repo_lock_token = remote_tokens
670
            assert self._lock_token, 'Remote server did not return a token!'
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
671
            # TODO: We really, really, really don't want to call _ensure_real
672
            # here, but it's the easiest way to ensure coherency between the
673
            # state of the RemoteBranch and RemoteRepository objects and the
674
            # physical locks.  If we don't materialise the real objects here,
675
            # then getting everything in the right state later is complex, so
676
            # for now we just do it the lazy way.
677
            #   -- Andrew Bennetts, 2007-02-22.
678
            self._ensure_real()
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
679
            if self._real_branch is not None:
680
                self._real_branch.lock_write(tokens=remote_tokens)
681
            if tokens is not None:
682
                self._leave_lock = True
683
            else:
684
                # XXX: this case seems to be unreachable; tokens cannot be None.
685
                self._leave_lock = False
686
            self._lock_mode = 'w'
687
            self._lock_count = 1
688
        elif self._lock_mode == 'r':
689
            raise errors.ReadOnlyTransaction
690
        else:
691
            if tokens is not None:
692
                # Tokens were given to lock_write, and we're relocking, so check
693
                # that the given tokens actually match the ones we already have.
694
                held_tokens = (self._lock_token, self._repo_lock_token)
695
                if tokens != held_tokens:
696
                    raise errors.TokenMismatch(str(tokens), str(held_tokens))
697
            self._lock_count += 1
698
        return self._lock_token, self._repo_lock_token
699
700
    def _unlock(self, branch_token, repo_token):
701
        path = self.bzrdir._path_for_remote_call(self._client)
702
        response = self._client.call('Branch.unlock', path, branch_token,
703
                                     repo_token)
704
        if response == ('ok',):
705
            return
706
        elif response[0] == 'TokenMismatch':
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
707
            raise errors.TokenMismatch(
708
                str((branch_token, repo_token)), '(remote tokens)')
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
709
        else:
710
            assert False, 'unexpected response code %s' % (response,)
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
711
712
    def unlock(self):
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
713
        self._lock_count -= 1
714
        if not self._lock_count:
715
            mode = self._lock_mode
716
            self._lock_mode = None
717
            if self._real_branch is not None:
718
                self._real_branch.unlock()
719
            if mode != 'w':
720
                return
721
            assert self._lock_token, 'Locked, but no token!'
722
            branch_token = self._lock_token
723
            repo_token = self._repo_lock_token
724
            self._lock_token = None
725
            self._repo_lock_token = None
726
            if not self._leave_lock:
727
                self._unlock(branch_token, repo_token)
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
728
729
    def break_lock(self):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
730
        self._ensure_real()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
731
        return self._real_branch.break_lock()
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
732
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
733
    def leave_lock_in_place(self):
734
        self._leave_lock = True
735
736
    def dont_leave_lock_in_place(self):
737
        self._leave_lock = False
738
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
739
    def last_revision_info(self):
740
        """See Branch.last_revision_info()."""
741
        path = self.bzrdir._path_for_remote_call(self._client)
742
        response = self._client.call('Branch.last_revision_info', path)
2018.5.52 by Wouter van Heyst
Provide more information when encountering unexpected responses from a smart
743
        assert response[0] == 'ok', 'unexpected response code %s' % (response,)
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
744
        revno = int(response[1])
745
        last_revision = response[2].decode('utf8')
746
        if last_revision == '':
747
            last_revision = NULL_REVISION
748
        return (revno, last_revision)
749
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
750
    def revision_history(self):
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
751
        """See Branch.revision_history()."""
752
        # XXX: TODO: this does not cache the revision history for the duration
753
        # of a lock, which is a bug - see the code for regular branches
754
        # for details.
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
755
        path = self.bzrdir._path_for_remote_call(self._client)
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
756
        response = self._client.call2('Branch.revision_history', path)
2018.5.52 by Wouter van Heyst
Provide more information when encountering unexpected responses from a smart
757
        assert response[0][0] == 'ok', 'unexpected response code %s' % (response[0],)
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
758
        result = response[1].read_body_bytes().decode('utf8').split('\x00')
759
        if result == ['']:
760
            return []
761
        return result
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
762
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
763
    @needs_write_lock
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
764
    def set_revision_history(self, rev_history):
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
765
        # Send just the tip revision of the history; the server will generate
766
        # the full history from that.  If the revision doesn't exist in this
767
        # branch, NoSuchRevision will be raised.
768
        path = self.bzrdir._path_for_remote_call(self._client)
769
        if rev_history == []:
770
            rev_id = ''
771
        else:
772
            rev_id = rev_history[-1]
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
773
        response = self._client.call('Branch.set_last_revision',
774
            path, self._lock_token, self._repo_lock_token, rev_id)
2018.12.3 by Andrew Bennetts
Add a Branch.set_last_revision smart method, and make RemoteBranch.set_revision_history use it.
775
        if response[0] == 'NoSuchRevision':
776
            raise NoSuchRevision(self, rev_id)
777
        else:
778
            assert response == ('ok',), (
779
                'unexpected response code %r' % (response,))
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
780
781
    def get_parent(self):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
782
        self._ensure_real()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
783
        return self._real_branch.get_parent()
784
        
1752.2.63 by Andrew Bennetts
Delegate set_parent.
785
    def set_parent(self, url):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
786
        self._ensure_real()
1752.2.63 by Andrew Bennetts
Delegate set_parent.
787
        return self._real_branch.set_parent(url)
788
        
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
789
    def get_config(self):
790
        return RemoteBranchConfig(self)
791
792
    @needs_write_lock
793
    def append_revision(self, *revision_ids):
794
        self._ensure_real()
795
        return self._real_branch.append_revision(*revision_ids)
796
797
    @needs_write_lock
798
    def pull(self, source, overwrite=False, stop_revision=None):
799
        self._ensure_real()
800
        self._real_branch.pull(
801
            source, overwrite=overwrite, stop_revision=stop_revision)
802
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
803
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
804
class RemoteWorkingTree(object):
805
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
806
    def __init__(self, remote_bzrdir, real_workingtree):
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
807
        self.real_workingtree = real_workingtree
808
        self.bzrdir = remote_bzrdir
809
810
    def __getattr__(self, name):
811
        # XXX: temporary way to lazily delegate everything to the real
812
        # workingtree
813
        return getattr(self.real_workingtree, name)
814
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
815
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
816
class RemoteBranchConfig(BranchConfig):
817
818
    def username(self):
819
        self.branch._ensure_real()
820
        return self.branch._real_branch.get_config().username()
821
2018.14.2 by Andrew Bennetts
All but one repository_implementation tests for RemoteRepository passing.
822
    def _get_branch_data_config(self):
823
        self.branch._ensure_real()
824
        if self._branch_data_config is None:
825
            self._branch_data_config = TreeConfig(self.branch._real_branch)
826
        return self._branch_data_config
827