/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)')
318
        else:
319
            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.
320
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
321
    def lock_write(self, token=None):
322
        if not self._lock_mode:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
323
            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
324
            assert self._lock_token, 'Remote server did not return a token!'
325
            if self._real_repository is not None:
326
                self._real_repository.lock_write(token=self._lock_token)
327
            if token is not None:
328
                self._leave_lock = True
329
            else:
330
                self._leave_lock = False
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
331
            self._lock_mode = 'w'
332
            self._lock_count = 1
333
        elif self._lock_mode == 'r':
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
334
            raise errors.ReadOnlyError(self)
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
335
        else:
336
            self._lock_count += 1
337
        return self._lock_token
338
339
    def leave_lock_in_place(self):
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
340
        self._leave_lock = True
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
341
342
    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
343
        self._leave_lock = False
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
344
345
    def _set_real_repository(self, repository):
346
        """Set the _real_repository for this repository.
347
348
        :param repository: The repository to fallback to for non-hpss
349
            implemented operations.
350
        """
351
        self._real_repository = repository
352
        if self._lock_mode == 'w':
353
            # if we are already locked, the real repository must be able to
354
            # acquire the lock with our token.
355
            self._real_repository.lock_write(self._lock_token)
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
356
        elif self._lock_mode == 'r':
357
            self._real_repository.lock_read()
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
358
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
359
    def _unlock(self, token):
360
        path = self.bzrdir._path_for_remote_call(self._client)
361
        response = self._client.call('Repository.unlock', path, token)
362
        if response == ('ok',):
363
            return
364
        elif response[0] == 'TokenMismatch':
365
            raise errors.TokenMismatch(token, '(remote token)')
366
        else:
367
            assert False, 'unexpected response code %s' % (response,)
368
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
369
    def unlock(self):
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
370
        self._lock_count -= 1
371
        if not self._lock_count:
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
372
            mode = self._lock_mode
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
373
            self._lock_mode = None
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
374
            if self._real_repository is not None:
375
                self._real_repository.unlock()
376
            if mode != 'w':
377
                return
378
            assert self._lock_token, 'Locked, but no token!'
379
            token = self._lock_token
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
380
            self._lock_token = None
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
381
            if not self._leave_lock:
382
                self._unlock(token)
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
383
384
    def break_lock(self):
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
385
        # should hand off to the network
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
386
        self._ensure_real()
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
387
        return self._real_repository.break_lock()
388
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
389
    ### These methods are just thin shims to the VFS object for now.
390
391
    def revision_tree(self, revision_id):
392
        self._ensure_real()
393
        return self._real_repository.revision_tree(revision_id)
394
395
    def get_commit_builder(self, branch, parents, config, timestamp=None,
396
                           timezone=None, committer=None, revprops=None,
397
                           revision_id=None):
398
        # FIXME: It ought to be possible to call this without immediately
399
        # triggering _ensure_real.  For now it's the easiest thing to do.
400
        self._ensure_real()
401
        builder = self._real_repository.get_commit_builder(branch, parents,
402
                config, timestamp=timestamp, timezone=timezone,
403
                committer=committer, revprops=revprops, revision_id=revision_id)
404
        # Make the builder use this RemoteRepository rather than the real one.
405
        builder.repository = self
406
        return builder
407
408
    @needs_write_lock
409
    def add_inventory(self, revid, inv, parents):
410
        self._ensure_real()
411
        return self._real_repository.add_inventory(revid, inv, parents)
412
413
    @needs_write_lock
414
    def add_revision(self, rev_id, rev, inv=None, config=None):
415
        self._ensure_real()
416
        return self._real_repository.add_revision(
417
            rev_id, rev, inv=inv, config=config)
418
419
    @needs_read_lock
420
    def get_inventory(self, revision_id):
421
        self._ensure_real()
422
        return self._real_repository.get_inventory(revision_id)
423
424
    @needs_read_lock
425
    def get_revision(self, revision_id):
426
        self._ensure_real()
427
        return self._real_repository.get_revision(revision_id)
428
429
    @property
430
    def weave_store(self):
431
        self._ensure_real()
432
        return self._real_repository.weave_store
433
434
    def get_transaction(self):
435
        self._ensure_real()
436
        return self._real_repository.get_transaction()
437
438
    @needs_read_lock
439
    def clone(self, a_bzrdir, revision_id=None, basis=None):
440
        self._ensure_real()
441
        return self._real_repository.clone(
442
            a_bzrdir, revision_id=revision_id, basis=basis)
443
444
    def make_working_trees(self):
445
        return False
446
447
    def fetch(self, source, revision_id=None, pb=None):
448
        self._ensure_real()
449
        return self._real_repository.fetch(
450
            source, revision_id=revision_id, pb=pb)
451
452
    @property
453
    def control_weaves(self):
454
        self._ensure_real()
455
        return self._real_repository.control_weaves
456
457
    @needs_read_lock
458
    def get_ancestry(self, revision_id):
459
        self._ensure_real()
460
        return self._real_repository.get_ancestry(revision_id)
461
462
    @needs_read_lock
463
    def get_inventory_weave(self):
464
        self._ensure_real()
465
        return self._real_repository.get_inventory_weave()
466
467
    def fileids_altered_by_revision_ids(self, revision_ids):
468
        self._ensure_real()
469
        return self._real_repository.fileids_altered_by_revision_ids(revision_ids)
470
471
    @needs_read_lock
472
    def get_signature_text(self, revision_id):
473
        self._ensure_real()
474
        return self._real_repository.get_signature_text(revision_id)
475
476
    @needs_read_lock
477
    def get_revision_graph_with_ghosts(self, revision_ids=None):
478
        self._ensure_real()
479
        return self._real_repository.get_revision_graph_with_ghosts(
480
            revision_ids=revision_ids)
481
482
    @needs_read_lock
483
    def get_inventory_xml(self, revision_id):
484
        self._ensure_real()
485
        return self._real_repository.get_inventory_xml(revision_id)
486
487
    def deserialise_inventory(self, revision_id, xml):
488
        self._ensure_real()
489
        return self._real_repository.deserialise_inventory(revision_id, xml)
490
491
    def reconcile(self, other=None, thorough=False):
492
        self._ensure_real()
493
        return self._real_repository.reconcile(other=other, thorough=thorough)
494
        
495
    def all_revision_ids(self):
496
        self._ensure_real()
497
        return self._real_repository.all_revision_ids()
498
    
499
    @needs_read_lock
500
    def get_deltas_for_revisions(self, revisions):
501
        self._ensure_real()
502
        return self._real_repository.get_deltas_for_revisions(revisions)
503
504
    @needs_read_lock
505
    def get_revision_delta(self, revision_id):
506
        self._ensure_real()
507
        return self._real_repository.get_revision_delta(revision_id)
508
509
    @needs_read_lock
510
    def revision_trees(self, revision_ids):
511
        self._ensure_real()
512
        return self._real_repository.revision_trees(revision_ids)
513
514
    @needs_read_lock
515
    def get_revision_reconcile(self, revision_id):
516
        self._ensure_real()
517
        return self._real_repository.get_revision_reconcile(revision_id)
518
519
    @needs_read_lock
520
    def check(self, revision_ids):
521
        self._ensure_real()
522
        return self._real_repository.check(revision_ids)
523
524
    def copy_content_into(self, destination, revision_id=None, basis=None):
525
        self._ensure_real()
526
        return self._real_repository.copy_content_into(
527
            destination, revision_id=revision_id, basis=basis)
528
529
    def set_make_working_trees(self, new_value):
530
        raise NotImplementedError(self.set_make_working_trees)
531
532
    @needs_write_lock
533
    def sign_revision(self, revision_id, gpg_strategy):
534
        self._ensure_real()
535
        return self._real_repository.sign_revision(revision_id, gpg_strategy)
536
537
    @needs_read_lock
538
    def get_revisions(self, revision_ids):
539
        self._ensure_real()
540
        return self._real_repository.get_revisions(revision_ids)
541
542
    def supports_rich_root(self):
2018.5.84 by Andrew Bennetts
Merge in supports-rich-root, another test passing.
543
        self._ensure_real()
544
        return self._real_repository.supports_rich_root()
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
545
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
546
    def iter_reverse_revision_history(self, revision_id):
547
        self._ensure_real()
548
        return self._real_repository.iter_reverse_revision_history(revision_id)
549
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
550
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
551
class RemoteBranchLockableFiles(object):
552
    """A 'LockableFiles' implementation that talks to a smart server.
553
    
554
    This is not a public interface class.
555
    """
556
557
    def __init__(self, bzrdir, _client):
558
        self.bzrdir = bzrdir
559
        self._client = _client
560
561
    def get(self, path):
562
        """'get' a remote path as per the LockableFiles interface.
563
564
        :param path: the file to 'get'. If this is 'branch.conf', we do not
565
             just retrieve a file, instead we ask the smart server to generate
566
             a configuration for us - which is retrieved as an INI file.
567
        """
568
        assert path == 'branch.conf'
569
        path = self.bzrdir._path_for_remote_call(self._client)
570
        response = self._client.call2('Branch.get_config_file', path)
2018.10.1 by v.ladeuil+lp at free
Line too long
571
        assert response[0][0] == 'ok', \
572
            'unexpected response code %s' % (response[0],)
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
573
        return StringIO(response[1].read_body_bytes())
574
575
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
576
class RemoteBranchFormat(branch.BranchFormat):
577
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
578
    def get_format_description(self):
579
        return 'Remote BZR Branch'
580
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
581
    def get_format_string(self):
582
        return 'Remote BZR Branch'
583
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
584
    def open(self, a_bzrdir):
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
585
        assert isinstance(a_bzrdir, RemoteBzrDir)
586
        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.
587
588
    def initialize(self, a_bzrdir):
589
        assert isinstance(a_bzrdir, RemoteBzrDir)
590
        return a_bzrdir.create_branch()
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
591
592
593
class RemoteBranch(branch.Branch):
594
    """Branch stored on a server accessed by HPSS RPC.
595
596
    At the moment most operations are mapped down to simple file operations.
597
    """
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
598
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
599
    def __init__(self, remote_bzrdir, remote_repository, real_branch=None,
600
        _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.
601
        """Create a RemoteBranch instance.
602
603
        :param real_branch: An optional local implementation of the branch
604
            format, usually accessing the data via the VFS.
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
605
        :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.
606
        """
1752.2.64 by Andrew Bennetts
Improve how RemoteBzrDir.open_branch works to handle references and not double-open repositories.
607
        self.bzrdir = remote_bzrdir
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
608
        if _client is not None:
609
            self._client = _client
610
        else:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
611
            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.
612
        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.
613
        if real_branch is not None:
614
            self._real_branch = real_branch
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
615
            # Give the remote repository the matching real repo.
616
            self.repository._set_real_repository(self._real_branch.repository)
617
            # Give the branch the remote repository to let fast-pathing happen.
618
            self._real_branch.repository = self.repository
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
619
        else:
620
            self._real_branch = None
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
621
        # 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.
622
        self._format = RemoteBranchFormat()
2018.5.55 by Robert Collins
Give RemoteBranch a base url in line with the Branch protocol.
623
        self.base = self.bzrdir.root_transport.base
2018.5.59 by Robert Collins
Get BranchConfig working somewhat on RemoteBranches (Robert Collins, Vincent Ladeuil).
624
        self.control_files = RemoteBranchLockableFiles(self.bzrdir, self._client)
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
625
        self._lock_mode = None
626
        self._lock_token = None
627
        self._lock_count = 0
628
        self._leave_lock = False
1752.2.64 by Andrew Bennetts
Improve how RemoteBzrDir.open_branch works to handle references and not double-open repositories.
629
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
630
    def _ensure_real(self):
631
        """Ensure that there is a _real_branch set.
632
633
        used before calls to self._real_branch.
634
        """
635
        if not self._real_branch:
636
            assert vfs.vfs_enabled()
637
            self.bzrdir._ensure_real()
638
            self._real_branch = self.bzrdir._real_bzrdir.open_branch()
2018.5.75 by Andrew Bennetts
Add Repository.{dont_,}leave_lock_in_place.
639
            # Give the remote repository the matching real repo.
640
            self.repository._set_real_repository(self._real_branch.repository)
641
            # 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.
642
            self._real_branch.repository = self.repository
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
643
            # XXX: deal with _lock_mode == 'w'
644
            if self._lock_mode == 'r':
645
                self._real_branch.lock_read()
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
646
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
647
    def get_physical_lock_status(self):
648
        """See Branch.get_physical_lock_status()."""
649
        # 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.
650
        self._ensure_real()
2018.5.60 by Robert Collins
More missing methods from RemoteBranch and RemoteRepository to let 'info' get further.
651
        return self._real_branch.get_physical_lock_status()
652
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
653
    def lock_read(self):
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
654
        if not self._lock_mode:
655
            self._lock_mode = 'r'
656
            self._lock_count = 1
657
            if self._real_branch is not None:
658
                self._real_branch.lock_read()
659
        else:
660
            self._lock_count += 1
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
661
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
662
    def _remote_lock_write(self, tokens):
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
663
        if tokens is None:
664
            branch_token = repo_token = ''
665
        else:
666
            branch_token, repo_token = tokens
667
        path = self.bzrdir._path_for_remote_call(self._client)
668
        response = self._client.call('Branch.lock_write', path, branch_token,
669
                                     repo_token)
670
        if response[0] == 'ok':
671
            ok, branch_token, repo_token = response
672
            return branch_token, repo_token
673
        elif response[0] == 'LockContention':
674
            raise errors.LockContention('(remote lock)')
675
        elif response[0] == 'TokenMismatch':
676
            raise errors.TokenMismatch(tokens, '(remote tokens)')
677
        else:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
678
            assert False, 'unexpected response code %r' % (response,)
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
679
            
680
    def lock_write(self, tokens=None):
681
        if not self._lock_mode:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
682
            remote_tokens = self._remote_lock_write(tokens)
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
683
            self._lock_token, self._repo_lock_token = remote_tokens
684
            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.
685
            # TODO: We really, really, really don't want to call _ensure_real
686
            # here, but it's the easiest way to ensure coherency between the
687
            # state of the RemoteBranch and RemoteRepository objects and the
688
            # physical locks.  If we don't materialise the real objects here,
689
            # then getting everything in the right state later is complex, so
690
            # for now we just do it the lazy way.
691
            #   -- Andrew Bennetts, 2007-02-22.
692
            self._ensure_real()
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
693
            if self._real_branch is not None:
694
                self._real_branch.lock_write(tokens=remote_tokens)
695
            if tokens is not None:
696
                self._leave_lock = True
697
            else:
698
                # XXX: this case seems to be unreachable; tokens cannot be None.
699
                self._leave_lock = False
700
            self._lock_mode = 'w'
701
            self._lock_count = 1
702
        elif self._lock_mode == 'r':
703
            raise errors.ReadOnlyTransaction
704
        else:
705
            if tokens is not None:
706
                # Tokens were given to lock_write, and we're relocking, so check
707
                # that the given tokens actually match the ones we already have.
708
                held_tokens = (self._lock_token, self._repo_lock_token)
709
                if tokens != held_tokens:
710
                    raise errors.TokenMismatch(str(tokens), str(held_tokens))
711
            self._lock_count += 1
712
        return self._lock_token, self._repo_lock_token
713
714
    def _unlock(self, branch_token, repo_token):
715
        path = self.bzrdir._path_for_remote_call(self._client)
716
        response = self._client.call('Branch.unlock', path, branch_token,
717
                                     repo_token)
718
        if response == ('ok',):
719
            return
720
        elif response[0] == 'TokenMismatch':
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
721
            raise errors.TokenMismatch(
722
                str((branch_token, repo_token)), '(remote tokens)')
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
723
        else:
724
            assert False, 'unexpected response code %s' % (response,)
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
725
726
    def unlock(self):
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
727
        self._lock_count -= 1
728
        if not self._lock_count:
729
            mode = self._lock_mode
730
            self._lock_mode = None
731
            if self._real_branch is not None:
2018.15.1 by Andrew Bennetts
All branch_implementations/test_locking tests passing.
732
                if not self._leave_lock:
733
                    # If this RemoteBranch will remove the physical lock for the
734
                    # repository, make sure the _real_branch doesn't do it
735
                    # first.  (Because the _real_branch's repository is set to
736
                    # be the RemoteRepository.)
737
                    self._real_branch.repository.leave_lock_in_place()
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
738
                self._real_branch.unlock()
739
            if mode != 'w':
740
                return
741
            assert self._lock_token, 'Locked, but no token!'
742
            branch_token = self._lock_token
743
            repo_token = self._repo_lock_token
744
            self._lock_token = None
745
            self._repo_lock_token = None
746
            if not self._leave_lock:
747
                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.
748
749
    def break_lock(self):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
750
        self._ensure_real()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
751
        return self._real_branch.break_lock()
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
752
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
753
    def leave_lock_in_place(self):
754
        self._leave_lock = True
755
756
    def dont_leave_lock_in_place(self):
757
        self._leave_lock = False
758
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
759
    def last_revision_info(self):
760
        """See Branch.last_revision_info()."""
761
        path = self.bzrdir._path_for_remote_call(self._client)
762
        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
763
        assert response[0] == 'ok', 'unexpected response code %s' % (response,)
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
764
        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.
765
        last_revision = response[2]
2018.5.51 by Wouter van Heyst
Test and implement RemoteBranch.last_revision_info()
766
        if last_revision == '':
767
            last_revision = NULL_REVISION
768
        return (revno, last_revision)
769
1752.2.31 by Martin Pool
[broken] some support for write operations over hpss
770
    def revision_history(self):
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
771
        """See Branch.revision_history()."""
772
        # XXX: TODO: this does not cache the revision history for the duration
773
        # of a lock, which is a bug - see the code for regular branches
774
        # for details.
2018.5.42 by Robert Collins
Various hopefully improvements, but wsgi is broken, handing over to spiv :).
775
        path = self.bzrdir._path_for_remote_call(self._client)
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
776
        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
777
        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.
778
        result = response[1].read_body_bytes().split('\x00')
2018.5.38 by Robert Collins
Implement RemoteBranch.revision_history().
779
        if result == ['']:
780
            return []
781
        return result
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
782
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
783
    @needs_write_lock
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
784
    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.
785
        # Send just the tip revision of the history; the server will generate
786
        # the full history from that.  If the revision doesn't exist in this
787
        # branch, NoSuchRevision will be raised.
788
        path = self.bzrdir._path_for_remote_call(self._client)
789
        if rev_history == []:
790
            rev_id = ''
791
        else:
792
            rev_id = rev_history[-1]
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
793
        response = self._client.call('Branch.set_last_revision',
794
            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.
795
        if response[0] == 'NoSuchRevision':
796
            raise NoSuchRevision(self, rev_id)
797
        else:
798
            assert response == ('ok',), (
799
                '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.
800
801
    def get_parent(self):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
802
        self._ensure_real()
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
803
        return self._real_branch.get_parent()
804
        
1752.2.63 by Andrew Bennetts
Delegate set_parent.
805
    def set_parent(self, url):
2018.5.70 by Robert Collins
Only try to get real repositories when an operation requires them.
806
        self._ensure_real()
1752.2.63 by Andrew Bennetts
Delegate set_parent.
807
        return self._real_branch.set_parent(url)
808
        
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
809
    def get_config(self):
810
        return RemoteBranchConfig(self)
811
2018.5.94 by Andrew Bennetts
Various small changes in aid of making tests pass (including deleting one invalid test).
812
    def sprout(self, to_bzrdir, revision_id=None):
813
        # Like Branch.sprout, except that it sprouts a branch in the default
814
        # format, because RemoteBranches can't be created at arbitrary URLs.
815
        # XXX: if to_bzrdir is a RemoteBranch, this should perhaps do
816
        # to_bzrdir.create_branch...
817
        self._ensure_real()
818
        result = branch.BranchFormat.get_default_format().initialize(to_bzrdir)
819
        self._real_branch.copy_content_into(result, revision_id=revision_id)
820
        result.set_parent(self.bzrdir.root_transport.base)
821
        return result
822
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
823
    @needs_write_lock
824
    def append_revision(self, *revision_ids):
825
        self._ensure_real()
826
        return self._real_branch.append_revision(*revision_ids)
827
828
    @needs_write_lock
829
    def pull(self, source, overwrite=False, stop_revision=None):
830
        self._ensure_real()
831
        self._real_branch.pull(
832
            source, overwrite=overwrite, stop_revision=stop_revision)
833
2018.14.3 by Andrew Bennetts
Make a couple more branch_implementations tests pass.
834
    @needs_read_lock
835
    def push(self, target, overwrite=False, stop_revision=None):
836
        self._ensure_real()
837
        self._real_branch.push(
838
            target, overwrite=overwrite, stop_revision=stop_revision)
839
840
    def is_locked(self):
841
        return self._lock_count >= 1
842
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
843
    def set_last_revision_info(self, revno, revision_id):
844
        self._ensure_real()
845
        return self._real_branch.set_last_revision_info(revno, revision_id)
846
1752.2.30 by Martin Pool
Start adding a RemoteBzrDir, etc
847
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
848
class RemoteWorkingTree(object):
849
1752.2.72 by Andrew Bennetts
Make Remote* classes in remote.py more consistent and remove some dead code.
850
    def __init__(self, remote_bzrdir, real_workingtree):
1752.2.50 by Andrew Bennetts
Implement RemoteBzrDir.create_{branch,workingtree}
851
        self.real_workingtree = real_workingtree
852
        self.bzrdir = remote_bzrdir
853
854
    def __getattr__(self, name):
855
        # XXX: temporary way to lazily delegate everything to the real
856
        # workingtree
857
        return getattr(self.real_workingtree, name)
858
1752.2.52 by Andrew Bennetts
Flesh out more Remote* methods needed to open and initialise remote branches/trees/repositories.
859
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
860
class RemoteBranchConfig(BranchConfig):
861
862
    def username(self):
863
        self.branch._ensure_real()
864
        return self.branch._real_branch.get_config().username()
865
2018.14.2 by Andrew Bennetts
All but one repository_implementation tests for RemoteRepository passing.
866
    def _get_branch_data_config(self):
867
        self.branch._ensure_real()
868
        if self._branch_data_config is None:
869
            self._branch_data_config = TreeConfig(self.branch._real_branch)
870
        return self._branch_data_config
871