/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.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()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
192
        self._lock_mode = None
193
        self._lock_token = None
194
        self._lock_count = 0
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
195
        self._leave_lock = False
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
196
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
197
    def _ensure_real(self):
198
        """Ensure that there is a _real_repository set.
199
200
        used before calls to self._real_repository.
201
        """
202
        if not self._real_repository:
203
            self.bzrdir._ensure_real()
204
            self._real_repository = self.bzrdir._real_bzrdir.open_repository()
205
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
206
    def get_revision_graph(self, revision_id=None):
207
        """See Repository.get_revision_graph()."""
208
        if revision_id is None:
209
            revision_id = ''
210
        elif revision_id == NULL_REVISION:
211
            return {}
212
213
        path = self.bzrdir._path_for_remote_call(self._client)
214
        response = self._client.call2('Repository.get_revision_graph', path, revision_id.encode('utf8'))
215
        assert response[0][0] in ('ok', 'nosuchrevision'), 'unexpected response code %s' % (response[0],)
216
        if response[0][0] == 'ok':
217
            coded = response[1].read_body_bytes()
218
            lines = coded.decode('utf8').split('\n')
219
            revision_graph = {}
220
            # FIXME
221
            for line in lines:
222
                d = list(line.split())
223
                revision_graph[d[0]] = d[1:]
224
                
225
            return revision_graph
226
        else:
227
            assert response[1] != ''
228
            raise NoSuchRevision(self, revision_id)
229
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
230
    def has_revision(self, revision_id):
231
        """See Repository.has_revision()."""
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
232
        path = self.bzrdir._path_for_remote_call(self._client)
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
233
        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
234
        assert response[0] in ('ok', 'no'), 'unexpected response code %s' % (response,)
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
235
        return response[0] == 'ok'
236
2018.5.62 by Robert Collins
Stub out RemoteRepository.gather_stats while its implemented in parallel.
237
    def gather_stats(self, revid, committers=None):
238
        """See Repository.gather_stats()."""
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
239
        path = self.bzrdir._path_for_remote_call(self._client)
240
        if revid in (None, NULL_REVISION):
241
            fmt_revid = ''
242
        else:
243
            fmt_revid = revid.encode('utf8')
244
        if committers is None:
245
            fmt_committers = 'no'
246
        else:
247
            fmt_committers = 'yes'
248
        response = self._client.call2('Repository.gather_stats', path,
249
                                      fmt_revid, fmt_committers)
250
        assert response[0][0] == 'ok', \
251
            'unexpected response code %s' % (response[0],)
252
253
        body = response[1].read_body_bytes()
254
        result = {}
255
        for line in body.split('\n'):
256
            if not line:
257
                continue
258
            key, val_text = line.split(':')
259
            if key in ('revisions', 'size', 'committers'):
260
                result[key] = int(val_text)
261
            elif key in ('firstrev', 'latestrev'):
262
                values = val_text.split(' ')[1:]
263
                result[key] = (float(values[0]), long(values[1]))
264
265
        return result
2018.5.62 by Robert Collins
Stub out RemoteRepository.gather_stats while its implemented in parallel.
266
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
267
    def get_physical_lock_status(self):
268
        """See Repository.get_physical_lock_status()."""
269
        return False
270
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
271
    def is_shared(self):
272
        """See Repository.is_shared()."""
273
        path = self.bzrdir._path_for_remote_call(self._client)
274
        response = self._client.call('Repository.is_shared', path)
275
        assert response[0] in ('yes', 'no'), 'unexpected response code %s' % (response,)
276
        return response[0] == 'yes'
277
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
278
    def lock_read(self):
279
        # wrong eventually - want a local lock cache context
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
280
        if not self._lock_mode:
281
            self._lock_mode = 'r'
282
            self._lock_count = 1
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
283
            if self._real_repository is not None:
284
                self._real_repository.lock_read()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
285
        else:
286
            self._lock_count += 1
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
287
288
    def _lock_write(self, token):
289
        path = self.bzrdir._path_for_remote_call(self._client)
290
        if token is None:
291
            token = ''
292
        response = self._client.call('Repository.lock_write', path, token)
293
        if response[0] == 'ok':
294
            ok, token = response
295
            return token
296
        elif response[0] == 'LockContention':
297
            raise errors.LockContention('(remote lock)')
298
        else:
299
            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.
300
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
301
    def lock_write(self, token=None):
302
        if not self._lock_mode:
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
303
            self._lock_token = self._lock_write(token)
304
            assert self._lock_token, 'Remote server did not return a token!'
305
            if self._real_repository is not None:
306
                self._real_repository.lock_write(token=self._lock_token)
307
            if token is not None:
308
                self._leave_lock = True
309
            else:
310
                self._leave_lock = False
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
311
            self._lock_mode = 'w'
312
            self._lock_count = 1
313
        elif self._lock_mode == 'r':
314
            raise errors.ReadOnlyTransaction
315
        else:
316
            self._lock_count += 1
317
        return self._lock_token
318
319
    def leave_lock_in_place(self):
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
320
        self._leave_lock = True
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
321
322
    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
323
        self._leave_lock = False
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
324
325
    def _set_real_repository(self, repository):
326
        """Set the _real_repository for this repository.
327
328
        :param repository: The repository to fallback to for non-hpss
329
            implemented operations.
330
        """
331
        self._real_repository = repository
332
        if self._lock_mode == 'w':
333
            # if we are already locked, the real repository must be able to
334
            # acquire the lock with our token.
335
            self._real_repository.lock_write(self._lock_token)
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
336
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
337
    def _unlock(self, token):
338
        path = self.bzrdir._path_for_remote_call(self._client)
339
        response = self._client.call('Repository.unlock', path, token)
340
        if response == ('ok',):
341
            return
342
        elif response[0] == 'TokenMismatch':
343
            raise errors.TokenMismatch(token, '(remote token)')
344
        else:
345
            assert False, 'unexpected response code %s' % (response,)
346
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
347
    def unlock(self):
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
348
        self._lock_count -= 1
349
        if not self._lock_count:
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
350
            mode = self._lock_mode
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
351
            self._lock_mode = None
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
352
            if self._real_repository is not None:
353
                self._real_repository.unlock()
354
            if mode != 'w':
355
                return
356
            assert self._lock_token, 'Locked, but no token!'
357
            token = self._lock_token
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
358
            self._lock_token = None
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
359
            if not self._leave_lock:
360
                self._unlock(token)
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
361
362
    def break_lock(self):
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
363
        # should hand off to the network
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
364
        self._ensure_real()
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
365
        return self._real_repository.break_lock()
366
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
367
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
368
class RemoteBranchLockableFiles(object):
369
    """A 'LockableFiles' implementation that talks to a smart server.
370
    
371
    This is not a public interface class.
372
    """
373
374
    def __init__(self, bzrdir, _client):
375
        self.bzrdir = bzrdir
376
        self._client = _client
377
378
    def get(self, path):
379
        """'get' a remote path as per the LockableFiles interface.
380
381
        :param path: the file to 'get'. If this is 'branch.conf', we do not
382
             just retrieve a file, instead we ask the smart server to generate
383
             a configuration for us - which is retrieved as an INI file.
384
        """
385
        assert path == 'branch.conf'
386
        path = self.bzrdir._path_for_remote_call(self._client)
387
        response = self._client.call2('Branch.get_config_file', path)
2018.10.1 by v.ladeuil+lp at free
Line too long
388
        assert response[0][0] == 'ok', \
389
            'unexpected response code %s' % (response[0],)
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
390
        return StringIO(response[1].read_body_bytes())
391
392
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
393
class RemoteBranchFormat(branch.BranchFormat):
394
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
395
    def get_format_description(self):
396
        return 'Remote BZR Branch'
397
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
398
    def open(self, a_bzrdir):
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
399
        assert isinstance(a_bzrdir, RemoteBzrDir)
400
        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.
401
402
    def initialize(self, a_bzrdir):
403
        assert isinstance(a_bzrdir, RemoteBzrDir)
404
        return a_bzrdir.create_branch()
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
405
406
407
class RemoteBranch(branch.Branch):
408
    """Branch stored on a server accessed by HPSS RPC.
409
410
    At the moment most operations are mapped down to simple file operations.
411
    """
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
412
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
413
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
414
        _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.
415
        """Create a RemoteBranch instance.
416
417
        :param real_branch: An optional local implementation of the branch
418
            format, usually accessing the data via the VFS.
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
419
        :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.
420
        """
1752.2.64 by Andrew Bennetts
Improve how RemoteBzrDir.open_branch works to handle references and not double-open repositories.
421
        self.bzrdir = remote_bzrdir
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
422
        if _client is not None:
423
            self._client = _client
424
        else:
425
            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.
426
        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.
427
        if real_branch is not None:
428
            self._real_branch = real_branch
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
429
        else:
430
            self._real_branch = None
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
431
        # 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.
432
        self._format = RemoteBranchFormat()
2018.5.55 by Robert Collins
Give RemoteBranch a base url in line with the Branch protocol.
433
        self.base = self.bzrdir.root_transport.base
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
434
        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.
435
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
436
    def _ensure_real(self):
437
        """Ensure that there is a _real_branch set.
438
439
        used before calls to self._real_branch.
440
        """
441
        if not self._real_branch:
442
            assert vfs.vfs_enabled()
443
            self.bzrdir._ensure_real()
444
            self._real_branch = self.bzrdir._real_bzrdir.open_branch()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
445
            # Give the remote repository the matching real repo.
446
            self.repository._set_real_repository(self._real_branch.repository)
447
            # 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.
448
            self._real_branch.repository = self.repository
449
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
450
    def get_physical_lock_status(self):
451
        """See Branch.get_physical_lock_status()."""
452
        # 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.
453
        self._ensure_real()
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
454
        return self._real_branch.get_physical_lock_status()
455
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
456
    def lock_read(self):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
457
        self._ensure_real()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
458
        return self._real_branch.lock_read()
459
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
460
    def lock_write(self, token=None):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
461
        self._ensure_real()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
462
        return self._real_branch.lock_write(token=token)
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
463
464
    def unlock(self):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
465
        self._ensure_real()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
466
        return self._real_branch.unlock()
467
468
    def break_lock(self):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
469
        self._ensure_real()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
470
        return self._real_branch.break_lock()
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
471
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
472
    def last_revision_info(self):
473
        """See Branch.last_revision_info()."""
474
        path = self.bzrdir._path_for_remote_call(self._client)
475
        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
476
        assert response[0] == 'ok', 'unexpected response code %s' % (response,)
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
477
        revno = int(response[1])
478
        last_revision = response[2].decode('utf8')
479
        if last_revision == '':
480
            last_revision = NULL_REVISION
481
        return (revno, last_revision)
482
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
483
    def revision_history(self):
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
484
        """See Branch.revision_history()."""
485
        # XXX: TODO: this does not cache the revision history for the duration
486
        # of a lock, which is a bug - see the code for regular branches
487
        # for details.
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
488
        path = self.bzrdir._path_for_remote_call(self._client)
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
489
        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
490
        assert response[0][0] == 'ok', 'unexpected response code %s' % (response[0],)
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
491
        result = response[1].read_body_bytes().decode('utf8').split('\x00')
492
        if result == ['']:
493
            return []
494
        return result
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
495
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
496
    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.
497
        # Send just the tip revision of the history; the server will generate
498
        # the full history from that.  If the revision doesn't exist in this
499
        # branch, NoSuchRevision will be raised.
500
        path = self.bzrdir._path_for_remote_call(self._client)
501
        if rev_history == []:
502
            rev_id = ''
503
        else:
504
            rev_id = rev_history[-1]
505
        response = self._client.call('Branch.set_last_revision', path, rev_id)
506
        if response[0] == 'NoSuchRevision':
507
            raise NoSuchRevision(self, rev_id)
508
        else:
509
            assert response == ('ok',), (
510
                '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.
511
512
    def get_parent(self):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
513
        self._ensure_real()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
514
        return self._real_branch.get_parent()
515
        
1752.2.63 by Andrew Bennetts
Delegate set_parent.
516
    def set_parent(self, url):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
517
        self._ensure_real()
1752.2.63 by Andrew Bennetts
Delegate set_parent.
518
        return self._real_branch.set_parent(url)
519
        
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
520
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
521
class RemoteWorkingTree(object):
522
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
523
    def __init__(self, remote_bzrdir, real_workingtree):
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
524
        self.real_workingtree = real_workingtree
525
        self.bzrdir = remote_bzrdir
526
527
    def __getattr__(self, name):
528
        # XXX: temporary way to lazily delegate everything to the real
529
        # workingtree
530
        return getattr(self.real_workingtree, name)
531
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
532