/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
2018.5.94 by Andrew Bennetts
Various small changes in aid of making tests pass (including deleting one invalid test).
141
    def clone(self, url, revision_id=None, basis=None, force_new_repo=False):
142
        self._ensure_real()
143
        return self._real_bzrdir.clone(url, revision_id=revision_id,
144
            basis=basis, force_new_repo=force_new_repo)
145
146
    #def sprout(self, url, revision_id=None, basis=None, force_new_repo=False):
147
    #    self._ensure_real()
148
    #    return self._real_bzrdir.sprout(url, revision_id=revision_id,
149
    #        basis=basis, force_new_repo=force_new_repo)
150
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
151
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
152
class RemoteRepositoryFormat(repository.RepositoryFormat):
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
153
    """Format for repositories accessed over rpc.
154
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
155
    Instances of this repository are represented by RemoteRepository
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
156
    instances.
157
    """
158
159
    _matchingbzrdir = RemoteBzrDirFormat
160
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
161
    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.
162
        assert isinstance(a_bzrdir, RemoteBzrDir)
163
        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.
164
    
165
    def open(self, a_bzrdir):
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
166
        assert isinstance(a_bzrdir, RemoteBzrDir)
167
        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.
168
169
    def get_format_description(self):
170
        return 'bzr remote repository'
171
172
    def __eq__(self, other):
1752.2.87 by Andrew Bennetts
Make tests pass.
173
        return self.__class__ == other.__class__
174
175
    rich_root_data = False
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
176
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
177
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
178
class RemoteRepository(object):
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
179
    """Repository accessed over rpc.
180
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
181
    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
182
    the transport.
183
    """
184
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
185
    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.
186
        """Create a RemoteRepository instance.
187
        
188
        :param remote_bzrdir: The bzrdir hosting this repository.
189
        :param real_repository: If not None, a local implementation of the
190
            repository logic for the repository, usually accessing the data
191
            via the VFS.
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
192
        :param _client: Private testing parameter - override the smart client
193
            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.
194
        """
195
        if real_repository:
2018.5.36 by Andrew Bennetts
Fix typo, and clean up some ununsed import warnings from pyflakes at the same time.
196
            self._real_repository = real_repository
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
197
        else:
198
            self._real_repository = None
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
199
        self.bzrdir = remote_bzrdir
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
200
        if _client is None:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
201
            self._client = client.SmartClient(self.bzrdir._medium)
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
202
        else:
203
            self._client = _client
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
204
        self._format = RemoteRepositoryFormat()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
205
        self._lock_mode = None
206
        self._lock_token = None
207
        self._lock_count = 0
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
208
        self._leave_lock = False
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
209
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
210
    def _ensure_real(self):
211
        """Ensure that there is a _real_repository set.
212
213
        used before calls to self._real_repository.
214
        """
215
        if not self._real_repository:
216
            self.bzrdir._ensure_real()
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
217
            #self._real_repository = self.bzrdir._real_bzrdir.open_repository()
218
            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.
219
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
220
    def get_revision_graph(self, revision_id=None):
221
        """See Repository.get_revision_graph()."""
222
        if revision_id is None:
223
            revision_id = ''
224
        elif revision_id == NULL_REVISION:
225
            return {}
226
227
        path = self.bzrdir._path_for_remote_call(self._client)
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
228
        assert type(revision_id) is str
229
        response = self._client.call2(
230
            'Repository.get_revision_graph', path, revision_id)
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
231
        assert response[0][0] in ('ok', 'nosuchrevision'), 'unexpected response code %s' % (response[0],)
232
        if response[0][0] == 'ok':
233
            coded = response[1].read_body_bytes()
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
234
            lines = coded.split('\n')
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
235
            revision_graph = {}
236
            # FIXME
237
            for line in lines:
238
                d = list(line.split())
239
                revision_graph[d[0]] = d[1:]
240
                
241
            return revision_graph
242
        else:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
243
            response_body = response[1].read_body_bytes()
244
            assert response_body == ''
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
245
            raise NoSuchRevision(self, revision_id)
246
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
247
    def has_revision(self, revision_id):
248
        """See Repository.has_revision()."""
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
249
        if revision_id is None:
250
            # The null revision is always present.
251
            return True
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
252
        path = self.bzrdir._path_for_remote_call(self._client)
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
253
        response = self._client.call('Repository.has_revision', path, revision_id)
2018.5.52 by Wouter van Heyst
Provide more information when encountering unexpected responses from a smart
254
        assert response[0] in ('ok', 'no'), 'unexpected response code %s' % (response,)
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
255
        return response[0] == 'ok'
256
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
257
    def gather_stats(self, revid=None, committers=None):
2018.5.62 by Robert Collins
Stub out RemoteRepository.gather_stats while its implemented in parallel.
258
        """See Repository.gather_stats()."""
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
259
        path = self.bzrdir._path_for_remote_call(self._client)
260
        if revid in (None, NULL_REVISION):
261
            fmt_revid = ''
262
        else:
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
263
            fmt_revid = revid
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
264
        if committers is None or not committers:
2018.10.3 by v.ladeuil+lp at free
more tests for gather_stats
265
            fmt_committers = 'no'
266
        else:
267
            fmt_committers = 'yes'
268
        response = self._client.call2('Repository.gather_stats', path,
269
                                      fmt_revid, fmt_committers)
270
        assert response[0][0] == 'ok', \
271
            'unexpected response code %s' % (response[0],)
272
273
        body = response[1].read_body_bytes()
274
        result = {}
275
        for line in body.split('\n'):
276
            if not line:
277
                continue
278
            key, val_text = line.split(':')
279
            if key in ('revisions', 'size', 'committers'):
280
                result[key] = int(val_text)
281
            elif key in ('firstrev', 'latestrev'):
282
                values = val_text.split(' ')[1:]
283
                result[key] = (float(values[0]), long(values[1]))
284
285
        return result
2018.5.62 by Robert Collins
Stub out RemoteRepository.gather_stats while its implemented in parallel.
286
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
287
    def get_physical_lock_status(self):
288
        """See Repository.get_physical_lock_status()."""
289
        return False
290
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
291
    def is_shared(self):
292
        """See Repository.is_shared()."""
293
        path = self.bzrdir._path_for_remote_call(self._client)
294
        response = self._client.call('Repository.is_shared', path)
295
        assert response[0] in ('yes', 'no'), 'unexpected response code %s' % (response,)
296
        return response[0] == 'yes'
297
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
298
    def lock_read(self):
299
        # wrong eventually - want a local lock cache context
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
300
        if not self._lock_mode:
301
            self._lock_mode = 'r'
302
            self._lock_count = 1
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
303
            if self._real_repository is not None:
304
                self._real_repository.lock_read()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
305
        else:
306
            self._lock_count += 1
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
307
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
308
    def _remote_lock_write(self, token):
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
309
        path = self.bzrdir._path_for_remote_call(self._client)
310
        if token is None:
311
            token = ''
312
        response = self._client.call('Repository.lock_write', path, token)
313
        if response[0] == 'ok':
314
            ok, token = response
315
            return token
316
        elif response[0] == 'LockContention':
317
            raise errors.LockContention('(remote lock)')
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
318
        elif response[0] == 'UnlockableTransport':
319
            raise errors.UnlockableTransport(self.bzrdir.root_transport)
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
320
        else:
321
            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.
322
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
323
    def lock_write(self, token=None):
324
        if not self._lock_mode:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
325
            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
326
            assert self._lock_token, 'Remote server did not return a token!'
327
            if self._real_repository is not None:
328
                self._real_repository.lock_write(token=self._lock_token)
329
            if token is not None:
330
                self._leave_lock = True
331
            else:
332
                self._leave_lock = False
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
333
            self._lock_mode = 'w'
334
            self._lock_count = 1
335
        elif self._lock_mode == 'r':
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
336
            raise errors.ReadOnlyError(self)
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
337
        else:
338
            self._lock_count += 1
339
        return self._lock_token
340
341
    def leave_lock_in_place(self):
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
342
        self._leave_lock = True
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
343
344
    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
345
        self._leave_lock = False
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
346
347
    def _set_real_repository(self, repository):
348
        """Set the _real_repository for this repository.
349
350
        :param repository: The repository to fallback to for non-hpss
351
            implemented operations.
352
        """
2018.5.97 by Andrew Bennetts
Fix more tests.
353
        assert not isinstance(repository, RemoteRepository)
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
354
        self._real_repository = repository
355
        if self._lock_mode == 'w':
356
            # if we are already locked, the real repository must be able to
357
            # acquire the lock with our token.
358
            self._real_repository.lock_write(self._lock_token)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
359
        elif self._lock_mode == 'r':
360
            self._real_repository.lock_read()
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
361
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
362
    def _unlock(self, token):
363
        path = self.bzrdir._path_for_remote_call(self._client)
364
        response = self._client.call('Repository.unlock', path, token)
365
        if response == ('ok',):
366
            return
367
        elif response[0] == 'TokenMismatch':
368
            raise errors.TokenMismatch(token, '(remote token)')
369
        else:
370
            assert False, 'unexpected response code %s' % (response,)
371
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
372
    def unlock(self):
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
373
        self._lock_count -= 1
374
        if not self._lock_count:
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
375
            mode = self._lock_mode
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
376
            self._lock_mode = None
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
377
            if self._real_repository is not None:
378
                self._real_repository.unlock()
379
            if mode != 'w':
380
                return
381
            assert self._lock_token, 'Locked, but no token!'
382
            token = self._lock_token
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
383
            self._lock_token = None
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
384
            if not self._leave_lock:
385
                self._unlock(token)
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
386
387
    def break_lock(self):
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
388
        # should hand off to the network
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
389
        self._ensure_real()
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
390
        return self._real_repository.break_lock()
391
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
392
    ### These methods are just thin shims to the VFS object for now.
393
394
    def revision_tree(self, revision_id):
395
        self._ensure_real()
396
        return self._real_repository.revision_tree(revision_id)
397
398
    def get_commit_builder(self, branch, parents, config, timestamp=None,
399
                           timezone=None, committer=None, revprops=None,
400
                           revision_id=None):
401
        # FIXME: It ought to be possible to call this without immediately
402
        # triggering _ensure_real.  For now it's the easiest thing to do.
403
        self._ensure_real()
404
        builder = self._real_repository.get_commit_builder(branch, parents,
405
                config, timestamp=timestamp, timezone=timezone,
406
                committer=committer, revprops=revprops, revision_id=revision_id)
407
        # Make the builder use this RemoteRepository rather than the real one.
408
        builder.repository = self
409
        return builder
410
411
    @needs_write_lock
412
    def add_inventory(self, revid, inv, parents):
413
        self._ensure_real()
414
        return self._real_repository.add_inventory(revid, inv, parents)
415
416
    @needs_write_lock
417
    def add_revision(self, rev_id, rev, inv=None, config=None):
418
        self._ensure_real()
419
        return self._real_repository.add_revision(
420
            rev_id, rev, inv=inv, config=config)
421
422
    @needs_read_lock
423
    def get_inventory(self, revision_id):
424
        self._ensure_real()
425
        return self._real_repository.get_inventory(revision_id)
426
427
    @needs_read_lock
428
    def get_revision(self, revision_id):
429
        self._ensure_real()
430
        return self._real_repository.get_revision(revision_id)
431
432
    @property
433
    def weave_store(self):
434
        self._ensure_real()
435
        return self._real_repository.weave_store
436
437
    def get_transaction(self):
438
        self._ensure_real()
439
        return self._real_repository.get_transaction()
440
441
    @needs_read_lock
442
    def clone(self, a_bzrdir, revision_id=None, basis=None):
443
        self._ensure_real()
444
        return self._real_repository.clone(
445
            a_bzrdir, revision_id=revision_id, basis=basis)
446
447
    def make_working_trees(self):
448
        return False
449
450
    def fetch(self, source, revision_id=None, pb=None):
451
        self._ensure_real()
452
        return self._real_repository.fetch(
453
            source, revision_id=revision_id, pb=pb)
454
455
    @property
456
    def control_weaves(self):
457
        self._ensure_real()
458
        return self._real_repository.control_weaves
459
460
    @needs_read_lock
461
    def get_ancestry(self, revision_id):
462
        self._ensure_real()
463
        return self._real_repository.get_ancestry(revision_id)
464
465
    @needs_read_lock
466
    def get_inventory_weave(self):
467
        self._ensure_real()
468
        return self._real_repository.get_inventory_weave()
469
470
    def fileids_altered_by_revision_ids(self, revision_ids):
471
        self._ensure_real()
472
        return self._real_repository.fileids_altered_by_revision_ids(revision_ids)
473
474
    @needs_read_lock
475
    def get_signature_text(self, revision_id):
476
        self._ensure_real()
477
        return self._real_repository.get_signature_text(revision_id)
478
479
    @needs_read_lock
480
    def get_revision_graph_with_ghosts(self, revision_ids=None):
481
        self._ensure_real()
482
        return self._real_repository.get_revision_graph_with_ghosts(
483
            revision_ids=revision_ids)
484
485
    @needs_read_lock
486
    def get_inventory_xml(self, revision_id):
487
        self._ensure_real()
488
        return self._real_repository.get_inventory_xml(revision_id)
489
490
    def deserialise_inventory(self, revision_id, xml):
491
        self._ensure_real()
492
        return self._real_repository.deserialise_inventory(revision_id, xml)
493
494
    def reconcile(self, other=None, thorough=False):
495
        self._ensure_real()
496
        return self._real_repository.reconcile(other=other, thorough=thorough)
497
        
498
    def all_revision_ids(self):
499
        self._ensure_real()
500
        return self._real_repository.all_revision_ids()
501
    
502
    @needs_read_lock
503
    def get_deltas_for_revisions(self, revisions):
504
        self._ensure_real()
505
        return self._real_repository.get_deltas_for_revisions(revisions)
506
507
    @needs_read_lock
508
    def get_revision_delta(self, revision_id):
509
        self._ensure_real()
510
        return self._real_repository.get_revision_delta(revision_id)
511
512
    @needs_read_lock
513
    def revision_trees(self, revision_ids):
514
        self._ensure_real()
515
        return self._real_repository.revision_trees(revision_ids)
516
517
    @needs_read_lock
518
    def get_revision_reconcile(self, revision_id):
519
        self._ensure_real()
520
        return self._real_repository.get_revision_reconcile(revision_id)
521
522
    @needs_read_lock
523
    def check(self, revision_ids):
524
        self._ensure_real()
525
        return self._real_repository.check(revision_ids)
526
527
    def copy_content_into(self, destination, revision_id=None, basis=None):
528
        self._ensure_real()
529
        return self._real_repository.copy_content_into(
530
            destination, revision_id=revision_id, basis=basis)
531
532
    def set_make_working_trees(self, new_value):
533
        raise NotImplementedError(self.set_make_working_trees)
534
535
    @needs_write_lock
536
    def sign_revision(self, revision_id, gpg_strategy):
537
        self._ensure_real()
538
        return self._real_repository.sign_revision(revision_id, gpg_strategy)
539
540
    @needs_read_lock
541
    def get_revisions(self, revision_ids):
542
        self._ensure_real()
543
        return self._real_repository.get_revisions(revision_ids)
544
545
    def supports_rich_root(self):
2018.5.84 by Andrew Bennetts
Merge in supports-rich-root, another test passing.
546
        self._ensure_real()
547
        return self._real_repository.supports_rich_root()
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
548
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
549
    def iter_reverse_revision_history(self, revision_id):
550
        self._ensure_real()
551
        return self._real_repository.iter_reverse_revision_history(revision_id)
552
2018.5.96 by Andrew Bennetts
Merge from bzr.dev, resolving the worst of the semantic conflicts, but there's
553
    @property
554
    def _serializer(self):
555
        self._ensure_real()
556
        return self._real_repository._serializer
557
2018.5.97 by Andrew Bennetts
Fix more tests.
558
    def store_revision_signature(self, gpg_strategy, plaintext, revision_id):
559
        self._ensure_real()
560
        return self._real_repository.store_revision_signature(
561
            gpg_strategy, plaintext, revision_id)
562
563
    def has_signature_for_revision_id(self, revision_id):
564
        self._ensure_real()
565
        return self._real_repository.has_signature_for_revision_id(revision_id)
566
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
567
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
568
class RemoteBranchLockableFiles(object):
569
    """A 'LockableFiles' implementation that talks to a smart server.
570
    
571
    This is not a public interface class.
572
    """
573
574
    def __init__(self, bzrdir, _client):
575
        self.bzrdir = bzrdir
576
        self._client = _client
577
578
    def get(self, path):
579
        """'get' a remote path as per the LockableFiles interface.
580
581
        :param path: the file to 'get'. If this is 'branch.conf', we do not
582
             just retrieve a file, instead we ask the smart server to generate
583
             a configuration for us - which is retrieved as an INI file.
584
        """
585
        assert path == 'branch.conf'
586
        path = self.bzrdir._path_for_remote_call(self._client)
587
        response = self._client.call2('Branch.get_config_file', path)
2018.10.1 by v.ladeuil+lp at free
Line too long
588
        assert response[0][0] == 'ok', \
589
            'unexpected response code %s' % (response[0],)
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
590
        return StringIO(response[1].read_body_bytes())
591
592
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
593
class RemoteBranchFormat(branch.BranchFormat):
594
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
595
    def get_format_description(self):
596
        return 'Remote BZR Branch'
597
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
598
    def get_format_string(self):
599
        return 'Remote BZR Branch'
600
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
601
    def open(self, a_bzrdir):
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
602
        assert isinstance(a_bzrdir, RemoteBzrDir)
603
        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.
604
605
    def initialize(self, a_bzrdir):
606
        assert isinstance(a_bzrdir, RemoteBzrDir)
607
        return a_bzrdir.create_branch()
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
608
609
610
class RemoteBranch(branch.Branch):
611
    """Branch stored on a server accessed by HPSS RPC.
612
613
    At the moment most operations are mapped down to simple file operations.
614
    """
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
615
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
616
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
617
        _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.
618
        """Create a RemoteBranch instance.
619
620
        :param real_branch: An optional local implementation of the branch
621
            format, usually accessing the data via the VFS.
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
622
        :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.
623
        """
1752.2.64 by Andrew Bennetts
Improve how RemoteBzrDir.open_branch works to handle references and not double-open repositories.
624
        self.bzrdir = remote_bzrdir
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
625
        if _client is not None:
626
            self._client = _client
627
        else:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
628
            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.
629
        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.
630
        if real_branch is not None:
631
            self._real_branch = real_branch
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
632
            # Give the remote repository the matching real repo.
2018.5.97 by Andrew Bennetts
Fix more tests.
633
            real_repo = self._real_branch.repository
634
            if isinstance(real_repo, RemoteRepository):
635
                real_repo._ensure_real()
636
                real_repo = real_repo._real_repository
637
            self.repository._set_real_repository(real_repo)
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
638
            # Give the branch the remote repository to let fast-pathing happen.
639
            self._real_branch.repository = self.repository
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
640
        else:
641
            self._real_branch = None
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
642
        # 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.
643
        self._format = RemoteBranchFormat()
2018.5.55 by Robert Collins
Give RemoteBranch a base url in line with the Branch protocol.
644
        self.base = self.bzrdir.root_transport.base
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
645
        self.control_files = RemoteBranchLockableFiles(self.bzrdir, self._client)
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
646
        self._lock_mode = None
647
        self._lock_token = None
648
        self._lock_count = 0
649
        self._leave_lock = False
1752.2.64 by Andrew Bennetts
Improve how RemoteBzrDir.open_branch works to handle references and not double-open repositories.
650
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
651
    def _ensure_real(self):
652
        """Ensure that there is a _real_branch set.
653
654
        used before calls to self._real_branch.
655
        """
656
        if not self._real_branch:
657
            assert vfs.vfs_enabled()
658
            self.bzrdir._ensure_real()
659
            self._real_branch = self.bzrdir._real_bzrdir.open_branch()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
660
            # Give the remote repository the matching real repo.
2018.5.97 by Andrew Bennetts
Fix more tests.
661
            real_repo = self._real_branch.repository
662
            if isinstance(real_repo, RemoteRepository):
663
                real_repo._ensure_real()
664
                real_repo = real_repo._real_repository
665
            self.repository._set_real_repository(real_repo)
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
666
            # 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.
667
            self._real_branch.repository = self.repository
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
668
            # XXX: deal with _lock_mode == 'w'
669
            if self._lock_mode == 'r':
670
                self._real_branch.lock_read()
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
671
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
672
    def get_physical_lock_status(self):
673
        """See Branch.get_physical_lock_status()."""
674
        # 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.
675
        self._ensure_real()
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
676
        return self._real_branch.get_physical_lock_status()
677
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
678
    def lock_read(self):
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
679
        if not self._lock_mode:
680
            self._lock_mode = 'r'
681
            self._lock_count = 1
682
            if self._real_branch is not None:
683
                self._real_branch.lock_read()
684
        else:
685
            self._lock_count += 1
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
686
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
687
    def _remote_lock_write(self, tokens):
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
688
        if tokens is None:
689
            branch_token = repo_token = ''
690
        else:
691
            branch_token, repo_token = tokens
692
        path = self.bzrdir._path_for_remote_call(self._client)
693
        response = self._client.call('Branch.lock_write', path, branch_token,
694
                                     repo_token)
695
        if response[0] == 'ok':
696
            ok, branch_token, repo_token = response
697
            return branch_token, repo_token
698
        elif response[0] == 'LockContention':
699
            raise errors.LockContention('(remote lock)')
700
        elif response[0] == 'TokenMismatch':
701
            raise errors.TokenMismatch(tokens, '(remote tokens)')
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
702
        elif response[0] == 'UnlockableTransport':
703
            raise errors.UnlockableTransport(self.bzrdir.root_transport)
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
704
        else:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
705
            assert False, 'unexpected response code %r' % (response,)
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
706
            
707
    def lock_write(self, tokens=None):
708
        if not self._lock_mode:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
709
            remote_tokens = self._remote_lock_write(tokens)
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
710
            self._lock_token, self._repo_lock_token = remote_tokens
711
            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.
712
            # TODO: We really, really, really don't want to call _ensure_real
713
            # here, but it's the easiest way to ensure coherency between the
714
            # state of the RemoteBranch and RemoteRepository objects and the
715
            # physical locks.  If we don't materialise the real objects here,
716
            # then getting everything in the right state later is complex, so
717
            # for now we just do it the lazy way.
718
            #   -- Andrew Bennetts, 2007-02-22.
719
            self._ensure_real()
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
720
            if self._real_branch is not None:
721
                self._real_branch.lock_write(tokens=remote_tokens)
722
            if tokens is not None:
723
                self._leave_lock = True
724
            else:
725
                # XXX: this case seems to be unreachable; tokens cannot be None.
726
                self._leave_lock = False
727
            self._lock_mode = 'w'
728
            self._lock_count = 1
729
        elif self._lock_mode == 'r':
730
            raise errors.ReadOnlyTransaction
731
        else:
732
            if tokens is not None:
733
                # Tokens were given to lock_write, and we're relocking, so check
734
                # that the given tokens actually match the ones we already have.
735
                held_tokens = (self._lock_token, self._repo_lock_token)
736
                if tokens != held_tokens:
737
                    raise errors.TokenMismatch(str(tokens), str(held_tokens))
738
            self._lock_count += 1
739
        return self._lock_token, self._repo_lock_token
740
741
    def _unlock(self, branch_token, repo_token):
742
        path = self.bzrdir._path_for_remote_call(self._client)
743
        response = self._client.call('Branch.unlock', path, branch_token,
744
                                     repo_token)
745
        if response == ('ok',):
746
            return
747
        elif response[0] == 'TokenMismatch':
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
748
            raise errors.TokenMismatch(
749
                str((branch_token, repo_token)), '(remote tokens)')
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
750
        else:
751
            assert False, 'unexpected response code %s' % (response,)
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
752
753
    def unlock(self):
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
754
        self._lock_count -= 1
755
        if not self._lock_count:
756
            mode = self._lock_mode
757
            self._lock_mode = None
758
            if self._real_branch is not None:
2018.15.1 by Andrew Bennetts
All branch_implementations/test_locking tests passing.
759
                if not self._leave_lock:
760
                    # If this RemoteBranch will remove the physical lock for the
761
                    # repository, make sure the _real_branch doesn't do it
762
                    # first.  (Because the _real_branch's repository is set to
763
                    # be the RemoteRepository.)
764
                    self._real_branch.repository.leave_lock_in_place()
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
765
                self._real_branch.unlock()
766
            if mode != 'w':
767
                return
768
            assert self._lock_token, 'Locked, but no token!'
769
            branch_token = self._lock_token
770
            repo_token = self._repo_lock_token
771
            self._lock_token = None
772
            self._repo_lock_token = None
773
            if not self._leave_lock:
774
                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.
775
776
    def break_lock(self):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
777
        self._ensure_real()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
778
        return self._real_branch.break_lock()
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
779
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
780
    def leave_lock_in_place(self):
781
        self._leave_lock = True
782
783
    def dont_leave_lock_in_place(self):
784
        self._leave_lock = False
785
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
786
    def last_revision_info(self):
787
        """See Branch.last_revision_info()."""
788
        path = self.bzrdir._path_for_remote_call(self._client)
789
        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
790
        assert response[0] == 'ok', 'unexpected response code %s' % (response,)
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
791
        revno = int(response[1])
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
792
        last_revision = response[2]
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
793
        if last_revision == '':
794
            last_revision = NULL_REVISION
795
        return (revno, last_revision)
796
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
797
    def revision_history(self):
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
798
        """See Branch.revision_history()."""
799
        # XXX: TODO: this does not cache the revision history for the duration
800
        # of a lock, which is a bug - see the code for regular branches
801
        # for details.
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
802
        path = self.bzrdir._path_for_remote_call(self._client)
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
803
        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
804
        assert response[0][0] == 'ok', 'unexpected response code %s' % (response[0],)
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
805
        result = response[1].read_body_bytes().split('\x00')
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
806
        if result == ['']:
807
            return []
808
        return result
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
809
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
810
    @needs_write_lock
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
811
    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.
812
        # Send just the tip revision of the history; the server will generate
813
        # the full history from that.  If the revision doesn't exist in this
814
        # branch, NoSuchRevision will be raised.
815
        path = self.bzrdir._path_for_remote_call(self._client)
816
        if rev_history == []:
817
            rev_id = ''
818
        else:
819
            rev_id = rev_history[-1]
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
820
        response = self._client.call('Branch.set_last_revision',
821
            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.
822
        if response[0] == 'NoSuchRevision':
823
            raise NoSuchRevision(self, rev_id)
824
        else:
825
            assert response == ('ok',), (
826
                '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.
827
828
    def get_parent(self):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
829
        self._ensure_real()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
830
        return self._real_branch.get_parent()
831
        
1752.2.63 by Andrew Bennetts
Delegate set_parent.
832
    def set_parent(self, url):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
833
        self._ensure_real()
1752.2.63 by Andrew Bennetts
Delegate set_parent.
834
        return self._real_branch.set_parent(url)
835
        
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
836
    def get_config(self):
837
        return RemoteBranchConfig(self)
838
2018.5.94 by Andrew Bennetts
Various small changes in aid of making tests pass (including deleting one invalid test).
839
    def sprout(self, to_bzrdir, revision_id=None):
840
        # Like Branch.sprout, except that it sprouts a branch in the default
841
        # format, because RemoteBranches can't be created at arbitrary URLs.
842
        # XXX: if to_bzrdir is a RemoteBranch, this should perhaps do
843
        # to_bzrdir.create_branch...
844
        self._ensure_real()
845
        result = branch.BranchFormat.get_default_format().initialize(to_bzrdir)
846
        self._real_branch.copy_content_into(result, revision_id=revision_id)
847
        result.set_parent(self.bzrdir.root_transport.base)
848
        return result
849
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
850
    @needs_write_lock
851
    def append_revision(self, *revision_ids):
852
        self._ensure_real()
853
        return self._real_branch.append_revision(*revision_ids)
854
855
    @needs_write_lock
856
    def pull(self, source, overwrite=False, stop_revision=None):
857
        self._ensure_real()
858
        self._real_branch.pull(
859
            source, overwrite=overwrite, stop_revision=stop_revision)
860
2018.14.3 by Andrew Bennetts
Make a couple more branch_implementations tests pass.
861
    @needs_read_lock
862
    def push(self, target, overwrite=False, stop_revision=None):
863
        self._ensure_real()
2018.5.97 by Andrew Bennetts
Fix more tests.
864
        return self._real_branch.push(
2018.14.3 by Andrew Bennetts
Make a couple more branch_implementations tests pass.
865
            target, overwrite=overwrite, stop_revision=stop_revision)
866
867
    def is_locked(self):
868
        return self._lock_count >= 1
869
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
870
    def set_last_revision_info(self, revno, revision_id):
871
        self._ensure_real()
872
        return self._real_branch.set_last_revision_info(revno, revision_id)
873
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
874
    def generate_revision_history(self, revision_id, last_rev=None,
875
                                  other_branch=None):
876
        self._ensure_real()
877
        return self._real_branch.generate_revision_history(
878
            revision_id, last_rev=last_rev, other_branch=other_branch)
879
2018.5.96 by Andrew Bennetts
Merge from bzr.dev, resolving the worst of the semantic conflicts, but there's
880
    @property
881
    def tags(self):
882
        self._ensure_real()
883
        return self._real_branch.tags
884
2018.5.97 by Andrew Bennetts
Fix more tests.
885
    def set_push_location(self, location):
886
        self._ensure_real()
887
        return self._real_branch.set_push_location(location)
888
889
    def update_revisions(self, other, stop_revision=None):
890
        self._ensure_real()
891
        return self._real_branch.update_revisions(
892
            other, stop_revision=stop_revision)
893
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
894
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
895
class RemoteWorkingTree(object):
896
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
897
    def __init__(self, remote_bzrdir, real_workingtree):
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
898
        self.real_workingtree = real_workingtree
899
        self.bzrdir = remote_bzrdir
900
901
    def __getattr__(self, name):
902
        # XXX: temporary way to lazily delegate everything to the real
903
        # workingtree
904
        return getattr(self.real_workingtree, name)
905
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
906
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
907
class RemoteBranchConfig(BranchConfig):
908
909
    def username(self):
910
        self.branch._ensure_real()
911
        return self.branch._real_branch.get_config().username()
912
2018.14.2 by Andrew Bennetts
All but one repository_implementation tests for RemoteRepository passing.
913
    def _get_branch_data_config(self):
914
        self.branch._ensure_real()
915
        if self._branch_data_config is None:
916
            self._branch_data_config = TreeConfig(self.branch._real_branch)
917
        return self._branch_data_config
918