~bzr-pqm/bzr/bzr.dev

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.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
26
from bzrlib.errors import NoSuchRevision
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
27
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.
28
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.
29
from bzrlib.urlutils import unescape
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
30
2018.5.25 by Andrew Bennetts
Make sure RemoteBzrDirFormat is always registered (John Arbash Meinel, Robert Collins, Andrew Bennetts).
31
# Note: RemoteBzrDirFormat is in bzrdir.py
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
32
33
class RemoteBzrDir(BzrDir):
34
    """Control directory on a remote server, accessed by HPSS."""
35
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
36
    def __init__(self, transport, _client=None):
37
        """Construct a RemoteBzrDir.
38
39
        :param _client: Private parameter for testing. Disables probing and the
40
            use of a real bzrdir.
41
        """
1752.2.39 by Martin Pool
[broken] implement upgrade apis on remote bzrdirs
42
        BzrDir.__init__(self, transport, RemoteBzrDirFormat())
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
43
        if _client is not None:
44
            self.client = _client
45
            return
46
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
47
        self.client = transport.get_smart_client()
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
48
        # this object holds a delegated bzrdir that uses file-level operations
49
        # 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.
50
        # XXX: We should go into find_format, but not allow it to find
51
        # 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.
52
        self._real_bzrdir = None
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
53
        
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
54
        self._ensure_real()
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
55
        smartclient = client.SmartClient(self.client)
56
        path = self._path_for_remote_call(smartclient)
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.
57
        #self._real_bzrdir._format.probe_transport(transport)
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
58
        response = smartclient.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.
59
        if response == ('no',):
60
            raise errors.NotBranchError(path=transport.base)
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
61
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
62
    def _ensure_real(self):
63
        """Ensure that there is a _real_bzrdir set.
64
65
        used before calls to self._real_bzrdir.
66
        """
67
        if not self._real_bzrdir:
68
            default_format = BzrDirFormat.get_default_format()
69
            self._real_bzrdir = default_format.open(self.root_transport,
70
                _found=True)
71
1752.2.39 by Martin Pool
[broken] implement upgrade apis on remote bzrdirs
72
    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.
73
        return RemoteRepository(
74
            self, self._real_bzrdir.create_repository(shared=shared))
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
75
76
    def create_branch(self):
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
77
        real_branch = self._real_bzrdir.create_branch()
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
78
        return RemoteBranch(self, self.find_repository(), real_branch)
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
79
80
    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.
81
        real_workingtree = self._real_bzrdir.create_workingtree(revision_id=revision_id)
82
        return RemoteWorkingTree(self, real_workingtree)
1752.2.39 by Martin Pool
[broken] implement upgrade apis on remote bzrdirs
83
2018.6.1 by Robert Collins
Implement a BzrDir.open_branch smart server method for opening a branch without VFS.
84
    def open_branch(self, _unsupported=False):
85
        assert _unsupported == False, 'unsupported flag support not implemented yet.'
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
86
        smartclient = client.SmartClient(self.client)
87
        path = self._path_for_remote_call(smartclient)
88
        response = smartclient.call('BzrDir.open_branch', path)
2018.5.52 by Wouter van Heyst
Provide more information when encountering unexpected responses from a smart
89
        assert response[0] == 'ok', 'unexpected response code %s' % (response,)
2018.6.1 by Robert Collins
Implement a BzrDir.open_branch smart server method for opening a branch without VFS.
90
        if response[0] != 'ok':
91
            # this should probably be a regular translate no ?
92
            raise errors.NotBranchError(path=self.root_transport.base)
93
        if response[1] == '':
94
            # branch at this location.
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
95
            return RemoteBranch(self, self.find_repository())
2018.6.1 by Robert Collins
Implement a BzrDir.open_branch smart server method for opening a branch without VFS.
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
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
101
    def open_repository(self):
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
102
        smartclient = client.SmartClient(self.client)
103
        path = self._path_for_remote_call(smartclient)
104
        response = smartclient.call('BzrDir.find_repository', path)
105
        assert response[0] in ('ok', 'norepository'), \
2018.5.52 by Wouter van Heyst
Provide more information when encountering unexpected responses from a smart
106
            'unexpected response code %s' % (response,)
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
107
        if response[0] == 'norepository':
108
            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.
109
        if response[1] == '':
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
110
            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.
111
        else:
112
            raise errors.NoRepositoryPresent(self)
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
113
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
114
    def open_workingtree(self):
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
115
        return RemoteWorkingTree(self, self._real_bzrdir.open_workingtree())
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
116
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
117
    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.
118
        """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 :).
119
        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.
120
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
121
    def get_branch_transport(self, branch_format):
122
        return self._real_bzrdir.get_branch_transport(branch_format)
123
1752.2.43 by Andrew Bennetts
Fix get_{branch,repository,workingtree}_transport.
124
    def get_repository_transport(self, repository_format):
125
        return self._real_bzrdir.get_repository_transport(repository_format)
126
127
    def get_workingtree_transport(self, workingtree_format):
128
        return self._real_bzrdir.get_workingtree_transport(workingtree_format)
129
1752.2.39 by Martin Pool
[broken] implement upgrade apis on remote bzrdirs
130
    def can_convert_format(self):
131
        """Upgrading of remote bzrdirs is not supported yet."""
132
        return False
133
134
    def needs_format_conversion(self, format=None):
135
        """Upgrading of remote bzrdirs is not supported yet."""
136
        return False
137
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
138
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
139
class RemoteRepositoryFormat(repository.RepositoryFormat):
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
140
    """Format for repositories accessed over rpc.
141
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
142
    Instances of this repository are represented by RemoteRepository
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
143
    instances.
144
    """
145
146
    _matchingbzrdir = RemoteBzrDirFormat
147
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
148
    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.
149
        assert isinstance(a_bzrdir, RemoteBzrDir)
150
        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.
151
    
152
    def open(self, a_bzrdir):
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
153
        assert isinstance(a_bzrdir, RemoteBzrDir)
154
        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.
155
156
    def get_format_description(self):
157
        return 'bzr remote repository'
158
159
    def __eq__(self, other):
1752.2.87 by Andrew Bennetts
Make tests pass.
160
        return self.__class__ == other.__class__
161
162
    rich_root_data = False
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
163
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
164
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
165
class RemoteRepository(object):
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
166
    """Repository accessed over rpc.
167
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
168
    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
169
    the transport.
170
    """
171
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
172
    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.
173
        """Create a RemoteRepository instance.
174
        
175
        :param remote_bzrdir: The bzrdir hosting this repository.
176
        :param real_repository: If not None, a local implementation of the
177
            repository logic for the repository, usually accessing the data
178
            via the VFS.
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
179
        :param _client: Private testing parameter - override the smart client
180
            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.
181
        """
182
        if real_repository:
2018.5.36 by Andrew Bennetts
Fix typo, and clean up some ununsed import warnings from pyflakes at the same time.
183
            self._real_repository = real_repository
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
184
        else:
185
            self._real_repository = None
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
186
        self.bzrdir = remote_bzrdir
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
187
        if _client is None:
188
            self._client = client.SmartClient(self.bzrdir.client)
189
        else:
190
            self._client = _client
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
191
        self._format = RemoteRepositoryFormat()
192
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
193
    def _ensure_real(self):
194
        """Ensure that there is a _real_repository set.
195
196
        used before calls to self._real_repository.
197
        """
198
        if not self._real_repository:
199
            self.bzrdir._ensure_real()
200
            self._real_repository = self.bzrdir._real_bzrdir.open_repository()
201
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
202
    def get_revision_graph(self, revision_id=None):
203
        """See Repository.get_revision_graph()."""
204
        if revision_id is None:
205
            revision_id = ''
206
        elif revision_id == NULL_REVISION:
207
            return {}
208
209
        path = self.bzrdir._path_for_remote_call(self._client)
210
        response = self._client.call2('Repository.get_revision_graph', path, revision_id.encode('utf8'))
211
        assert response[0][0] in ('ok', 'nosuchrevision'), 'unexpected response code %s' % (response[0],)
212
        if response[0][0] == 'ok':
213
            coded = response[1].read_body_bytes()
214
            lines = coded.decode('utf8').split('\n')
215
            revision_graph = {}
216
            # FIXME
217
            for line in lines:
218
                d = list(line.split())
219
                revision_graph[d[0]] = d[1:]
220
                
221
            return revision_graph
222
        else:
223
            assert response[1] != ''
224
            raise NoSuchRevision(self, revision_id)
225
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
226
    def has_revision(self, revision_id):
227
        """See Repository.has_revision()."""
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
228
        path = self.bzrdir._path_for_remote_call(self._client)
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
229
        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
230
        assert response[0] in ('ok', 'no'), 'unexpected response code %s' % (response,)
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
231
        return response[0] == 'ok'
232
2018.5.62 by Robert Collins
Stub out RemoteRepository.gather_stats while its implemented in parallel.
233
    def gather_stats(self, revid, committers=None):
234
        """See Repository.gather_stats()."""
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
235
        path = self.bzrdir._path_for_remote_call(self._client)
236
        if revid in (None, NULL_REVISION):
237
            fmt_revid = ''
238
        else:
239
            fmt_revid = revid.encode('utf8')
240
        if committers is None:
241
            fmt_committers = 'no'
242
        else:
243
            fmt_committers = 'yes'
244
        response = self._client.call2('Repository.gather_stats', path,
245
                                      fmt_revid, fmt_committers)
246
        assert response[0][0] == 'ok', \
247
            'unexpected response code %s' % (response[0],)
248
249
        body = response[1].read_body_bytes()
250
        result = {}
251
        for line in body.split('\n'):
252
            if not line:
253
                continue
254
            key, val_text = line.split(':')
255
            if key in ('revisions', 'size', 'committers'):
256
                result[key] = int(val_text)
257
            elif key in ('firstrev', 'latestrev'):
258
                values = val_text.split(' ')[1:]
259
                result[key] = (float(values[0]), long(values[1]))
260
261
        return result
2018.5.62 by Robert Collins
Stub out RemoteRepository.gather_stats while its implemented in parallel.
262
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
263
    def get_physical_lock_status(self):
264
        """See Repository.get_physical_lock_status()."""
265
        return False
266
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
267
    def is_shared(self):
268
        """See Repository.is_shared()."""
269
        path = self.bzrdir._path_for_remote_call(self._client)
270
        response = self._client.call('Repository.is_shared', path)
271
        assert response[0] in ('yes', 'no'), 'unexpected response code %s' % (response,)
272
        return response[0] == 'yes'
273
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
274
    def lock_read(self):
275
        # wrong eventually - want a local lock cache context
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
276
        self._ensure_real()
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
277
        return self._real_repository.lock_read()
278
279
    def lock_write(self):
280
        # definately wrong: want to check if there is a real repo
281
        # and not thunk through if not
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
282
        self._ensure_real()
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
283
        return self._real_repository.lock_write()
284
285
    def unlock(self):
286
        # should free cache context.
287
        return self._real_repository.unlock()
288
289
    def break_lock(self):
290
        # should hand off to the network - or better yet, we should not
291
        # allow stale network locks ?
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
292
        self._ensure_real()
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
293
        return self._real_repository.break_lock()
294
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
295
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
296
class RemoteBranchLockableFiles(object):
297
    """A 'LockableFiles' implementation that talks to a smart server.
298
    
299
    This is not a public interface class.
300
    """
301
302
    def __init__(self, bzrdir, _client):
303
        self.bzrdir = bzrdir
304
        self._client = _client
305
306
    def get(self, path):
307
        """'get' a remote path as per the LockableFiles interface.
308
309
        :param path: the file to 'get'. If this is 'branch.conf', we do not
310
             just retrieve a file, instead we ask the smart server to generate
311
             a configuration for us - which is retrieved as an INI file.
312
        """
313
        assert path == 'branch.conf'
314
        path = self.bzrdir._path_for_remote_call(self._client)
315
        response = self._client.call2('Branch.get_config_file', path)
2018.10.1 by v.ladeuil+lp at free
Line too long
316
        assert response[0][0] == 'ok', \
317
            'unexpected response code %s' % (response[0],)
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
318
        return StringIO(response[1].read_body_bytes())
319
320
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
321
class RemoteBranchFormat(branch.BranchFormat):
322
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
323
    def get_format_description(self):
324
        return 'Remote BZR Branch'
325
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
326
    def open(self, a_bzrdir):
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
327
        assert isinstance(a_bzrdir, RemoteBzrDir)
328
        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.
329
330
    def initialize(self, a_bzrdir):
331
        assert isinstance(a_bzrdir, RemoteBzrDir)
332
        return a_bzrdir.create_branch()
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
333
334
335
class RemoteBranch(branch.Branch):
336
    """Branch stored on a server accessed by HPSS RPC.
337
338
    At the moment most operations are mapped down to simple file operations.
339
    """
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
340
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
341
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
342
        _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.
343
        """Create a RemoteBranch instance.
344
345
        :param real_branch: An optional local implementation of the branch
346
            format, usually accessing the data via the VFS.
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
347
        :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.
348
        """
1752.2.64 by Andrew Bennetts
Improve how RemoteBzrDir.open_branch works to handle references and not double-open repositories.
349
        self.bzrdir = remote_bzrdir
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
350
        if _client is not None:
351
            self._client = _client
352
        else:
353
            self._client = client.SmartClient(self.bzrdir.client)
1752.2.64 by Andrew Bennetts
Improve how RemoteBzrDir.open_branch works to handle references and not double-open repositories.
354
        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.
355
        if real_branch is not None:
356
            self._real_branch = real_branch
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
357
        else:
358
            self._real_branch = None
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
359
        # 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.
360
        self._format = RemoteBranchFormat()
2018.5.55 by Robert Collins
Give RemoteBranch a base url in line with the Branch protocol.
361
        self.base = self.bzrdir.root_transport.base
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
362
        self.control_files = RemoteBranchLockableFiles(self.bzrdir, self._client)
1752.2.64 by Andrew Bennetts
Improve how RemoteBzrDir.open_branch works to handle references and not double-open repositories.
363
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
364
    def _ensure_real(self):
365
        """Ensure that there is a _real_branch set.
366
367
        used before calls to self._real_branch.
368
        """
369
        if not self._real_branch:
370
            assert vfs.vfs_enabled()
371
            self.bzrdir._ensure_real()
372
            self._real_branch = self.bzrdir._real_bzrdir.open_branch()
373
            # give the repository the matching file level repo.
374
            self.repository._real_repository = self._real_branch.repository
375
            # give the branch the remote repository to let fast-pathing happen
376
            self._real_branch.repository = self.repository
377
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
378
    def get_physical_lock_status(self):
379
        """See Branch.get_physical_lock_status()."""
380
        # 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.
381
        self._ensure_real()
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
382
        return self._real_branch.get_physical_lock_status()
383
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
384
    def lock_read(self):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
385
        self._ensure_real()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
386
        return self._real_branch.lock_read()
387
388
    def lock_write(self):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
389
        self._ensure_real()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
390
        return self._real_branch.lock_write()
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
391
392
    def unlock(self):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
393
        self._ensure_real()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
394
        return self._real_branch.unlock()
395
396
    def break_lock(self):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
397
        self._ensure_real()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
398
        return self._real_branch.break_lock()
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
399
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
400
    def last_revision_info(self):
401
        """See Branch.last_revision_info()."""
402
        path = self.bzrdir._path_for_remote_call(self._client)
403
        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
404
        assert response[0] == 'ok', 'unexpected response code %s' % (response,)
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
405
        revno = int(response[1])
406
        last_revision = response[2].decode('utf8')
407
        if last_revision == '':
408
            last_revision = NULL_REVISION
409
        return (revno, last_revision)
410
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
411
    def revision_history(self):
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
412
        """See Branch.revision_history()."""
413
        # XXX: TODO: this does not cache the revision history for the duration
414
        # of a lock, which is a bug - see the code for regular branches
415
        # for details.
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
416
        path = self.bzrdir._path_for_remote_call(self._client)
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
417
        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
418
        assert response[0][0] == 'ok', 'unexpected response code %s' % (response[0],)
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
419
        result = response[1].read_body_bytes().decode('utf8').split('\x00')
420
        if result == ['']:
421
            return []
422
        return result
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
423
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
424
    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.
425
        # Send just the tip revision of the history; the server will generate
426
        # the full history from that.  If the revision doesn't exist in this
427
        # branch, NoSuchRevision will be raised.
428
        path = self.bzrdir._path_for_remote_call(self._client)
429
        if rev_history == []:
430
            rev_id = ''
431
        else:
432
            rev_id = rev_history[-1]
433
        response = self._client.call('Branch.set_last_revision', path, rev_id)
434
        if response[0] == 'NoSuchRevision':
435
            raise NoSuchRevision(self, rev_id)
436
        else:
437
            assert response == ('ok',), (
438
                '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.
439
440
    def get_parent(self):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
441
        self._ensure_real()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
442
        return self._real_branch.get_parent()
443
        
1752.2.63 by Andrew Bennetts
Delegate set_parent.
444
    def set_parent(self, url):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
445
        self._ensure_real()
1752.2.63 by Andrew Bennetts
Delegate set_parent.
446
        return self._real_branch.set_parent(url)
447
        
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
448
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
449
class RemoteWorkingTree(object):
450
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
451
    def __init__(self, remote_bzrdir, real_workingtree):
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
452
        self.real_workingtree = real_workingtree
453
        self.bzrdir = remote_bzrdir
454
455
    def __getattr__(self, name):
456
        # XXX: temporary way to lazily delegate everything to the real
457
        # workingtree
458
        return getattr(self.real_workingtree, name)
459
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
460