/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
1
# Copyright (C) 2006, 2007 Canonical Ltd
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
16
17
"""Server-side repository related request implmentations."""
18
3211.5.2 by Robert Collins
Change RemoteRepository.get_parent_map to use bz2 not gzip for compression.
19
import bz2
2571.2.2 by Ian Clatworthy
use basename as poolie recommended
20
import os
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
21
import Queue
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
22
import sys
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
23
import tarfile
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
24
import tempfile
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
25
import threading
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
26
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
27
from bzrlib import (
2694.5.4 by Jelmer Vernooij
Move bzrlib.util.bencode to bzrlib._bencode_py.
28
    bencode,
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
29
    errors,
4070.9.2 by Andrew Bennetts
Rough prototype of allowing a SearchResult to be passed to fetch, and using that to improve network conversations.
30
    graph,
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
31
    osutils,
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
32
    pack,
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
33
    )
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
34
from bzrlib.bzrdir import BzrDir
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
35
from bzrlib.smart.request import (
36
    FailedSmartServerResponse,
37
    SmartServerRequest,
38
    SuccessfulSmartServerResponse,
39
    )
4419.2.5 by Andrew Bennetts
Add Repository.get_rev_id_for_revno, and use it both as the _ensure_real fallback and as the server-side implementation.
40
from bzrlib.repository import _strip_NULL_ghosts, network_format_registry
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
41
from bzrlib import revision as _mod_revision
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
42
from bzrlib.versionedfile import NetworkRecordStream, record_to_fulltext_bytes
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
43
44
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
45
class SmartServerRepositoryRequest(SmartServerRequest):
46
    """Common base class for Repository requests."""
47
48
    def do(self, path, *args):
49
        """Execute a repository request.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
50
2692.1.10 by Andrew Bennetts
More docstring polish
51
        All Repository requests take a path to the repository as their first
52
        argument.  The repository must be at the exact path given by the
53
        client - no searching is done.
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
54
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
55
        The actual logic is delegated to self.do_repository_request.
56
2692.1.10 by Andrew Bennetts
More docstring polish
57
        :param client_path: The path for the repository as received from the
58
            client.
59
        :return: A SmartServerResponse from self.do_repository_request().
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
60
        """
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
61
        transport = self.transport_from_client_path(path)
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
62
        bzrdir = BzrDir.open_from_transport(transport)
3184.1.10 by Robert Collins
Change the smart server verb for Repository.stream_revisions_chunked to use SearchResults as the request mechanism for downloads.
63
        # Save the repository for use with do_body.
64
        self._repository = bzrdir.open_repository()
65
        return self.do_repository_request(self._repository, *args)
66
67
    def do_repository_request(self, repository, *args):
68
        """Override to provide an implementation for a verb."""
69
        # No-op for verbs that take bodies (None as a result indicates a body
70
        # is expected)
71
        return None
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
72
4332.2.1 by Robert Collins
Fix bug 360791 by not raising an error when a smart server is asked for more content than it has locally; the client is assumed to be monitoring what it gets.
73
    def recreate_search(self, repository, search_bytes, discard_excess=False):
74
        """Recreate a search from its serialised form.
75
76
        :param discard_excess: If True, and the search refers to data we don't
77
            have, just silently accept that fact - the verb calling
78
            recreate_search trusts that clients will look for missing things
79
            they expected and get it from elsewhere.
80
        """
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
81
        lines = search_bytes.split('\n')
82
        if lines[0] == 'ancestry-of':
4070.9.14 by Andrew Bennetts
Tweaks requested by Robert's review.
83
            heads = lines[1:]
84
            search_result = graph.PendingAncestryResult(heads, repository)
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
85
            return search_result, None
86
        elif lines[0] == 'search':
4332.2.1 by Robert Collins
Fix bug 360791 by not raising an error when a smart server is asked for more content than it has locally; the client is assumed to be monitoring what it gets.
87
            return self.recreate_search_from_recipe(repository, lines[1:],
88
                discard_excess=discard_excess)
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
89
        else:
90
            return (None, FailedSmartServerResponse(('BadSearch',)))
91
4332.2.1 by Robert Collins
Fix bug 360791 by not raising an error when a smart server is asked for more content than it has locally; the client is assumed to be monitoring what it gets.
92
    def recreate_search_from_recipe(self, repository, lines,
93
        discard_excess=False):
94
        """Recreate a specific revision search (vs a from-tip search).
95
96
        :param discard_excess: If True, and the search refers to data we don't
97
            have, just silently accept that fact - the verb calling
98
            recreate_search trusts that clients will look for missing things
99
            they expected and get it from elsewhere.
100
        """
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
101
        start_keys = set(lines[0].split(' '))
102
        exclude_keys = set(lines[1].split(' '))
103
        revision_count = int(lines[2])
104
        repository.lock_read()
105
        try:
106
            search = repository.get_graph()._make_breadth_first_searcher(
107
                start_keys)
108
            while True:
109
                try:
110
                    next_revs = search.next()
111
                except StopIteration:
112
                    break
113
                search.stop_searching_any(exclude_keys.intersection(next_revs))
114
            search_result = search.get_result()
4332.2.1 by Robert Collins
Fix bug 360791 by not raising an error when a smart server is asked for more content than it has locally; the client is assumed to be monitoring what it gets.
115
            if (not discard_excess and
116
                search_result.get_recipe()[3] != revision_count):
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
117
                # we got back a different amount of data than expected, this
118
                # gets reported as NoSuchRevision, because less revisions
119
                # indicates missing revisions, and more should never happen as
120
                # the excludes list considers ghosts and ensures that ghost
121
                # filling races are not a problem.
122
                return (None, FailedSmartServerResponse(('NoSuchRevision',)))
4070.9.2 by Andrew Bennetts
Rough prototype of allowing a SearchResult to be passed to fetch, and using that to improve network conversations.
123
            return (search_result, None)
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
124
        finally:
125
            repository.unlock()
126
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
127
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
128
class SmartServerRepositoryReadLocked(SmartServerRepositoryRequest):
129
    """Calls self.do_readlocked_repository_request."""
130
131
    def do_repository_request(self, repository, *args):
132
        """Read lock a repository for do_readlocked_repository_request."""
133
        repository.lock_read()
134
        try:
135
            return self.do_readlocked_repository_request(repository, *args)
136
        finally:
137
            repository.unlock()
138
139
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
140
class SmartServerRepositoryGetParentMap(SmartServerRepositoryRequest):
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
141
    """Bzr 1.2+ - get parent data for revisions during a graph search."""
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
142
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
143
    no_extra_results = False
144
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
145
    def do_repository_request(self, repository, *revision_ids):
146
        """Get parent details for some revisions.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
147
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
148
        All the parents for revision_ids are returned. Additionally up to 64KB
149
        of additional parent data found by performing a breadth first search
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
150
        from revision_ids is returned. The verb takes a body containing the
151
        current search state, see do_body for details.
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
152
4190.1.5 by Robert Collins
Review tweaks.
153
        If 'include-missing:' is in revision_ids, ghosts encountered in the
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
154
        graph traversal for getting parent data are included in the result with
155
        a prefix of 'missing:'.
156
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
157
        :param repository: The repository to query in.
3172.5.8 by Robert Collins
Review feedback.
158
        :param revision_ids: The utf8 encoded revision_id to answer for.
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
159
        """
160
        self._revision_ids = revision_ids
161
        return None # Signal that we want a body.
162
163
    def do_body(self, body_bytes):
164
        """Process the current search state and perform the parent lookup.
165
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
166
        :return: A smart server response where the body contains an utf8
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
167
            encoded flattened list of the parents of the revisions (the same
3211.5.3 by Robert Collins
Adjust size of batch and change gzip comments to bzip2.
168
            format as Repository.get_revision_graph) which has been bz2
169
            compressed.
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
170
        """
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
171
        repository = self._repository
172
        repository.lock_read()
173
        try:
174
            return self._do_repository_request(body_bytes)
175
        finally:
176
            repository.unlock()
177
178
    def _do_repository_request(self, body_bytes):
179
        repository = self._repository
180
        revision_ids = set(self._revision_ids)
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
181
        include_missing = 'include-missing:' in revision_ids
182
        if include_missing:
183
            revision_ids.remove('include-missing:')
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
184
        body_lines = body_bytes.split('\n')
185
        search_result, error = self.recreate_search_from_recipe(
186
            repository, body_lines)
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
187
        if error is not None:
188
            return error
189
        # TODO might be nice to start up the search again; but thats not
190
        # written or tested yet.
4070.9.2 by Andrew Bennetts
Rough prototype of allowing a SearchResult to be passed to fetch, and using that to improve network conversations.
191
        client_seen_revs = set(search_result.get_keys())
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
192
        # Always include the requested ids.
193
        client_seen_revs.difference_update(revision_ids)
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
194
        lines = []
195
        repo_graph = repository.get_graph()
196
        result = {}
197
        queried_revs = set()
198
        size_so_far = 0
199
        next_revs = revision_ids
200
        first_loop_done = False
201
        while next_revs:
202
            queried_revs.update(next_revs)
203
            parent_map = repo_graph.get_parent_map(next_revs)
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
204
            current_revs = next_revs
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
205
            next_revs = set()
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
206
            for revision_id in current_revs:
207
                missing_rev = False
208
                parents = parent_map.get(revision_id)
209
                if parents is not None:
210
                    # adjust for the wire
211
                    if parents == (_mod_revision.NULL_REVISION,):
212
                        parents = ()
213
                    # prepare the next query
214
                    next_revs.update(parents)
215
                    encoded_id = revision_id
216
                else:
217
                    missing_rev = True
218
                    encoded_id = "missing:" + revision_id
219
                    parents = []
220
                if (revision_id not in client_seen_revs and
221
                    (not missing_rev or include_missing)):
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
222
                    # Client does not have this revision, give it to it.
223
                    # add parents to the result
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
224
                    result[encoded_id] = parents
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
225
                    # Approximate the serialized cost of this revision_id.
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
226
                    size_so_far += 2 + len(encoded_id) + sum(map(len, parents))
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
227
            # get all the directly asked for parents, and then flesh out to
228
            # 64K (compressed) or so. We do one level of depth at a time to
3211.5.3 by Robert Collins
Adjust size of batch and change gzip comments to bzip2.
229
            # stay in sync with the client. The 250000 magic number is
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
230
            # estimated compression ratio taken from bzr.dev itself.
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
231
            if self.no_extra_results or (
232
                first_loop_done and size_so_far > 250000):
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
233
                next_revs = set()
234
                break
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
235
            # don't query things we've already queried
236
            next_revs.difference_update(queried_revs)
237
            first_loop_done = True
238
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
239
        # sorting trivially puts lexographically similar revision ids together.
240
        # Compression FTW.
241
        for revision, parents in sorted(result.items()):
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
242
            lines.append(' '.join((revision, ) + tuple(parents)))
243
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
244
        return SuccessfulSmartServerResponse(
3211.5.2 by Robert Collins
Change RemoteRepository.get_parent_map to use bz2 not gzip for compression.
245
            ('ok', ), bz2.compress('\n'.join(lines)))
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
246
247
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
248
class SmartServerRepositoryGetRevisionGraph(SmartServerRepositoryReadLocked):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
249
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
250
    def do_readlocked_repository_request(self, repository, revision_id):
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
251
        """Return the result of repository.get_revision_graph(revision_id).
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
252
253
        Deprecated as of bzr 1.4, but supported for older clients.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
254
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
255
        :param repository: The repository to query in.
256
        :param revision_id: The utf8 encoded revision_id to get a graph from.
257
        :return: A smart server response where the body contains an utf8
258
            encoded flattened list of the revision graph.
259
        """
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
260
        if not revision_id:
261
            revision_id = None
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
262
263
        lines = []
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
264
        graph = repository.get_graph()
265
        if revision_id:
266
            search_ids = [revision_id]
267
        else:
268
            search_ids = repository.all_revision_ids()
269
        search = graph._make_breadth_first_searcher(search_ids)
270
        transitive_ids = set()
271
        map(transitive_ids.update, list(search))
272
        parent_map = graph.get_parent_map(transitive_ids)
3287.6.8 by Robert Collins
Reduce code duplication as per review.
273
        revision_graph = _strip_NULL_ghosts(parent_map)
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
274
        if revision_id and revision_id not in revision_graph:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
275
            # Note that we return an empty body, rather than omitting the body.
276
            # This way the client knows that it can always expect to find a body
277
            # in the response for this method, even in the error case.
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
278
            return FailedSmartServerResponse(('nosuchrevision', revision_id), '')
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
279
280
        for revision, parents in revision_graph.items():
2592.3.50 by Robert Collins
Merge bzr.dev.
281
            lines.append(' '.join((revision, ) + tuple(parents)))
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
282
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
283
        return SuccessfulSmartServerResponse(('ok', ), '\n'.join(lines))
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
284
285
4419.2.4 by Andrew Bennetts
Add Repository.get_rev_id_for_revno RPC, removes VFS calls from 'pull -r 123' case.
286
class SmartServerRepositoryGetRevIdForRevno(SmartServerRepositoryReadLocked):
287
288
    def do_readlocked_repository_request(self, repository, revno,
289
            known_pair):
290
        """Find the revid for a given revno, given a known revno/revid pair.
291
        
4419.2.16 by Andrew Bennetts
New in 1.17, not 1.16.
292
        New in 1.17.
4419.2.4 by Andrew Bennetts
Add Repository.get_rev_id_for_revno RPC, removes VFS calls from 'pull -r 123' case.
293
        """
4419.2.6 by Andrew Bennetts
Add tests for server-side logic, and fix the bugs exposed by those tests.
294
        try:
295
            found_flag, result = repository.get_rev_id_for_revno(revno, known_pair)
296
        except errors.RevisionNotPresent, err:
297
            if err.revision_id != known_pair[1]:
298
                raise AssertionError(
299
                    'get_rev_id_for_revno raised RevisionNotPresent for '
300
                    'non-initial revision: ' + err.revision_id)
301
            return FailedSmartServerResponse(
302
                ('nosuchrevision', err.revision_id))
4419.2.5 by Andrew Bennetts
Add Repository.get_rev_id_for_revno, and use it both as the _ensure_real fallback and as the server-side implementation.
303
        if found_flag:
304
            return SuccessfulSmartServerResponse(('ok', result))
305
        else:
306
            earliest_revno, earliest_revid = result
4419.2.4 by Andrew Bennetts
Add Repository.get_rev_id_for_revno RPC, removes VFS calls from 'pull -r 123' case.
307
            return SuccessfulSmartServerResponse(
4419.2.5 by Andrew Bennetts
Add Repository.get_rev_id_for_revno, and use it both as the _ensure_real fallback and as the server-side implementation.
308
                ('history-incomplete', earliest_revno, earliest_revid))
4419.2.4 by Andrew Bennetts
Add Repository.get_rev_id_for_revno RPC, removes VFS calls from 'pull -r 123' case.
309
310
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
311
class SmartServerRequestHasRevision(SmartServerRepositoryRequest):
312
313
    def do_repository_request(self, repository, revision_id):
314
        """Return ok if a specific revision is in the repository at path.
315
316
        :param repository: The repository to query in.
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
317
        :param revision_id: The utf8 encoded revision_id to lookup.
318
        :return: A smart server response of ('ok', ) if the revision is
319
            present.
320
        """
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
321
        if repository.has_revision(revision_id):
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
322
            return SuccessfulSmartServerResponse(('yes', ))
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
323
        else:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
324
            return SuccessfulSmartServerResponse(('no', ))
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
325
326
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
327
class SmartServerRepositoryGatherStats(SmartServerRepositoryRequest):
328
329
    def do_repository_request(self, repository, revid, committers):
330
        """Return the result of repository.gather_stats().
331
332
        :param repository: The repository to query in.
333
        :param revid: utf8 encoded rev id or an empty string to indicate None
334
        :param committers: 'yes' or 'no'.
335
336
        :return: A SmartServerResponse ('ok',), a encoded body looking like
337
              committers: 1
338
              firstrev: 1234.230 0
339
              latestrev: 345.700 3600
340
              revisions: 2
341
342
              But containing only fields returned by the gather_stats() call
343
        """
344
        if revid == '':
345
            decoded_revision_id = None
346
        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.
347
            decoded_revision_id = revid
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
348
        if committers == 'yes':
349
            decoded_committers = True
350
        else:
351
            decoded_committers = None
352
        stats = repository.gather_stats(decoded_revision_id, decoded_committers)
353
354
        body = ''
355
        if stats.has_key('committers'):
356
            body += 'committers: %d\n' % stats['committers']
357
        if stats.has_key('firstrev'):
358
            body += 'firstrev: %.3f %d\n' % stats['firstrev']
359
        if stats.has_key('latestrev'):
360
             body += 'latestrev: %.3f %d\n' % stats['latestrev']
361
        if stats.has_key('revisions'):
362
            body += 'revisions: %d\n' % stats['revisions']
363
        if stats.has_key('size'):
364
            body += 'size: %d\n' % stats['size']
365
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
366
        return SuccessfulSmartServerResponse(('ok', ), body)
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
367
368
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
369
class SmartServerRepositoryIsShared(SmartServerRepositoryRequest):
370
371
    def do_repository_request(self, repository):
372
        """Return the result of repository.is_shared().
373
374
        :param repository: The repository to query in.
375
        :return: A smart server response of ('yes', ) if the repository is
376
            shared, and ('no', ) if it is not.
377
        """
378
        if repository.is_shared():
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
379
            return SuccessfulSmartServerResponse(('yes', ))
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
380
        else:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
381
            return SuccessfulSmartServerResponse(('no', ))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
382
383
384
class SmartServerRepositoryLockWrite(SmartServerRepositoryRequest):
385
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
386
    def do_repository_request(self, repository, token=''):
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
387
        # XXX: this probably should not have a token.
388
        if token == '':
389
            token = None
390
        try:
391
            token = repository.lock_write(token=token)
392
        except errors.LockContention, e:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
393
            return FailedSmartServerResponse(('LockContention',))
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.
394
        except errors.UnlockableTransport:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
395
            return FailedSmartServerResponse(('UnlockableTransport',))
2872.5.3 by Martin Pool
Pass back LockFailed from smart server lock methods
396
        except errors.LockFailed, e:
397
            return FailedSmartServerResponse(('LockFailed',
398
                str(e.lock), str(e.why)))
3015.2.7 by Robert Collins
In the RemoteServer repository methods handle repositories that cannot be remotely locked like pack repositories, and add a read lock in SmartServerRepositoryStreamKnitDataForRevisions.
399
        if token is not None:
400
            repository.leave_lock_in_place()
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
401
        repository.unlock()
402
        if token is None:
403
            token = ''
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
404
        return SuccessfulSmartServerResponse(('ok', token))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
405
406
4060.1.5 by Robert Collins
Verb change name requested by Andrew.
407
class SmartServerRepositoryGetStream(SmartServerRepositoryRequest):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
408
409
    def do_repository_request(self, repository, to_network_name):
410
        """Get a stream for inserting into a to_format repository.
411
412
        :param repository: The repository to stream from.
413
        :param to_network_name: The network name of the format of the target
414
            repository.
415
        """
416
        self._to_format = network_format_registry.get(to_network_name)
417
        return None # Signal that we want a body.
418
419
    def do_body(self, body_bytes):
420
        repository = self._repository
421
        repository.lock_read()
422
        try:
4332.2.1 by Robert Collins
Fix bug 360791 by not raising an error when a smart server is asked for more content than it has locally; the client is assumed to be monitoring what it gets.
423
            search_result, error = self.recreate_search(repository, body_bytes,
424
                discard_excess=True)
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
425
            if error is not None:
426
                repository.unlock()
427
                return error
428
            source = repository._get_source(self._to_format)
4070.9.2 by Andrew Bennetts
Rough prototype of allowing a SearchResult to be passed to fetch, and using that to improve network conversations.
429
            stream = source.get_stream(search_result)
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
430
        except Exception:
431
            exc_info = sys.exc_info()
432
            try:
433
                # On non-error, unlocking is done by the body stream handler.
434
                repository.unlock()
435
            finally:
436
                raise exc_info[0], exc_info[1], exc_info[2]
437
        return SuccessfulSmartServerResponse(('ok',),
438
            body_stream=self.body_stream(stream, repository))
439
440
    def body_stream(self, stream, repository):
441
        byte_stream = _stream_to_byte_stream(stream, repository._format)
442
        try:
443
            for bytes in byte_stream:
444
                yield bytes
445
        except errors.RevisionNotPresent, e:
446
            # This shouldn't be able to happen, but as we don't buffer
447
            # everything it can in theory happen.
448
            repository.unlock()
449
            yield FailedSmartServerResponse(('NoSuchRevision', e.revision_id))
450
        else:
451
            repository.unlock()
452
453
454
def _stream_to_byte_stream(stream, src_format):
455
    """Convert a record stream to a self delimited byte stream."""
456
    pack_writer = pack.ContainerSerialiser()
457
    yield pack_writer.begin()
458
    yield pack_writer.bytes_record(src_format.network_name(), '')
459
    for substream_type, substream in stream:
460
        for record in substream:
461
            if record.storage_kind in ('chunked', 'fulltext'):
462
                serialised = record_to_fulltext_bytes(record)
4392.2.2 by John Arbash Meinel
Add tests that ensure we can fetch branches with ghosts in their ancestry.
463
            elif record.storage_kind == 'absent':
464
                raise ValueError("Absent factory for %s" % (record.key,))
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
465
            else:
466
                serialised = record.get_bytes_as(record.storage_kind)
467
            if serialised:
468
                # Some streams embed the whole stream into the wire
469
                # representation of the first record, which means that
470
                # later records have no wire representation: we skip them.
471
                yield pack_writer.bytes_record(serialised, [(substream_type,)])
472
    yield pack_writer.end()
473
474
475
def _byte_stream_to_stream(byte_stream):
4060.1.5 by Robert Collins
Verb change name requested by Andrew.
476
    """Convert a byte stream into a format and a stream.
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
477
478
    :param byte_stream: A bytes iterator, as output by _stream_to_byte_stream.
479
    :return: (RepositoryFormat, stream_generator)
480
    """
481
    stream_decoder = pack.ContainerPushParser()
482
    def record_stream():
483
        """Closure to return the substreams."""
484
        # May have fully parsed records already.
485
        for record in stream_decoder.read_pending_records():
486
            record_names, record_bytes = record
487
            record_name, = record_names
488
            substream_type = record_name[0]
489
            substream = NetworkRecordStream([record_bytes])
490
            yield substream_type, substream.read()
491
        for bytes in byte_stream:
492
            stream_decoder.accept_bytes(bytes)
493
            for record in stream_decoder.read_pending_records():
494
                record_names, record_bytes = record
495
                record_name, = record_names
496
                substream_type = record_name[0]
497
                substream = NetworkRecordStream([record_bytes])
498
                yield substream_type, substream.read()
499
    for bytes in byte_stream:
500
        stream_decoder.accept_bytes(bytes)
501
        for record in stream_decoder.read_pending_records(max=1):
502
            record_names, src_format_name = record
503
            src_format = network_format_registry.get(src_format_name)
504
            return src_format, record_stream()
505
506
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
507
class SmartServerRepositoryUnlock(SmartServerRepositoryRequest):
508
509
    def do_repository_request(self, repository, token):
510
        try:
511
            repository.lock_write(token=token)
512
        except errors.TokenMismatch, e:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
513
            return FailedSmartServerResponse(('TokenMismatch',))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
514
        repository.dont_leave_lock_in_place()
515
        repository.unlock()
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
516
        return SuccessfulSmartServerResponse(('ok',))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
517
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
518
4017.3.4 by Robert Collins
Create a verb for Repository.set_make_working_trees.
519
class SmartServerRepositorySetMakeWorkingTrees(SmartServerRepositoryRequest):
520
521
    def do_repository_request(self, repository, str_bool_new_value):
522
        if str_bool_new_value == 'True':
523
            new_value = True
524
        else:
525
            new_value = False
526
        repository.set_make_working_trees(new_value)
527
        return SuccessfulSmartServerResponse(('ok',))
528
529
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
530
class SmartServerRepositoryTarball(SmartServerRepositoryRequest):
2018.18.11 by Martin Pool
merge hpss changes
531
    """Get the raw repository files as a tarball.
532
533
    The returned tarball contains a .bzr control directory which in turn
534
    contains a repository.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
535
536
    This takes one parameter, compression, which currently must be
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
537
    "", "gz", or "bz2".
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
538
539
    This is used to implement the Repository.copy_content_into operation.
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
540
    """
541
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
542
    def do_repository_request(self, repository, compression):
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
543
        tmp_dirname, tmp_repo = self._copy_to_tempdir(repository)
2018.18.5 by Martin Pool
Repository.tarball locks repository while running for consistency
544
        try:
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
545
            controldir_name = tmp_dirname + '/.bzr'
546
            return self._tarfile_response(controldir_name, compression)
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
547
        finally:
548
            osutils.rmtree(tmp_dirname)
549
550
    def _copy_to_tempdir(self, from_repo):
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
551
        tmp_dirname = osutils.mkdtemp(prefix='tmpbzrclone')
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
552
        tmp_bzrdir = from_repo.bzrdir._format.initialize(tmp_dirname)
553
        tmp_repo = from_repo._format.initialize(tmp_bzrdir)
554
        from_repo.copy_content_into(tmp_repo)
555
        return tmp_dirname, tmp_repo
556
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
557
    def _tarfile_response(self, tmp_dirname, compression):
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
558
        temp = tempfile.NamedTemporaryFile()
559
        try:
2557.1.1 by Martin Pool
[BUG 119330] Fix tempfile permissions error in smart server tar bundling (under windows) (Martin_)
560
            self._tarball_of_dir(tmp_dirname, compression, temp.file)
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
561
            # all finished; write the tempfile out to the network
562
            temp.seek(0)
2420.2.2 by Andrew Bennetts
Merge tarball branch that's already with PQM, resolving conflicts.
563
            return SuccessfulSmartServerResponse(('ok',), temp.read())
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
564
            # FIXME: Don't read the whole thing into memory here; rather stream
565
            # it out from the file onto the network. mbp 20070411
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
566
        finally:
567
            temp.close()
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
568
2557.1.1 by Martin Pool
[BUG 119330] Fix tempfile permissions error in smart server tar bundling (under windows) (Martin_)
569
    def _tarball_of_dir(self, dirname, compression, ofile):
2571.2.2 by Ian Clatworthy
use basename as poolie recommended
570
        filename = os.path.basename(ofile.name)
571
        tarball = tarfile.open(fileobj=ofile, name=filename,
2571.2.1 by Ian Clatworthy
fix #123485 - selftest broken under Python 2.5.1 because of tafile API change
572
            mode='w|' + compression)
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
573
        try:
574
            # The tarball module only accepts ascii names, and (i guess)
575
            # packs them with their 8bit names.  We know all the files
576
            # within the repository have ASCII names so the should be safe
577
            # to pack in.
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
578
            dirname = dirname.encode(sys.getfilesystemencoding())
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
579
            # python's tarball module includes the whole path by default so
580
            # override it
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
581
            if not dirname.endswith('.bzr'):
582
                raise ValueError(dirname)
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
583
            tarball.add(dirname, '.bzr') # recursive by default
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
584
        finally:
585
            tarball.close()
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
586
587
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
588
class SmartServerRepositoryInsertStreamLocked(SmartServerRepositoryRequest):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
589
    """Insert a record stream from a RemoteSink into a repository.
590
591
    This gets bytes pushed to it by the network infrastructure and turns that
592
    into a bytes iterator using a thread. That is then processed by
593
    _byte_stream_to_stream.
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
594
595
    New in 1.14.
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
596
    """
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
597
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
598
    def do_repository_request(self, repository, resume_tokens, lock_token):
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
599
        """StreamSink.insert_stream for a remote repository."""
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
600
        repository.lock_write(token=lock_token)
601
        self.do_insert_stream_request(repository, resume_tokens)
602
603
    def do_insert_stream_request(self, repository, resume_tokens):
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
604
        tokens = [token for token in resume_tokens.split(' ') if token]
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
605
        self.tokens = tokens
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
606
        self.repository = repository
607
        self.queue = Queue.Queue()
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
608
        self.insert_thread = threading.Thread(target=self._inserter_thread)
609
        self.insert_thread.start()
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
610
611
    def do_chunk(self, body_stream_chunk):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
612
        self.queue.put(body_stream_chunk)
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
613
614
    def _inserter_thread(self):
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
615
        try:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
616
            src_format, stream = _byte_stream_to_stream(
617
                self.blocking_byte_stream())
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
618
            self.insert_result = self.repository._get_sink().insert_stream(
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
619
                stream, src_format, self.tokens)
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
620
            self.insert_ok = True
621
        except:
622
            self.insert_exception = sys.exc_info()
623
            self.insert_ok = False
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
624
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
625
    def blocking_byte_stream(self):
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
626
        while True:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
627
            bytes = self.queue.get()
628
            if bytes is StopIteration:
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
629
                return
630
            else:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
631
                yield bytes
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
632
633
    def do_end(self):
634
        self.queue.put(StopIteration)
635
        if self.insert_thread is not None:
636
            self.insert_thread.join()
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
637
        if not self.insert_ok:
638
            exc_info = self.insert_exception
639
            raise exc_info[0], exc_info[1], exc_info[2]
640
        write_group_tokens, missing_keys = self.insert_result
641
        if write_group_tokens or missing_keys:
642
            # bzip needed? missing keys should typically be a small set.
643
            # Should this be a streaming body response ?
644
            missing_keys = sorted(missing_keys)
645
            bytes = bencode.bencode((write_group_tokens, missing_keys))
646
            self.repository.unlock()
647
            return SuccessfulSmartServerResponse(('missing-basis', bytes))
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
648
        else:
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
649
            self.repository.unlock()
650
            return SuccessfulSmartServerResponse(('ok', ))
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
651
652
653
class SmartServerRepositoryInsertStream(SmartServerRepositoryInsertStreamLocked):
654
    """Insert a record stream from a RemoteSink into an unlocked repository.
655
656
    This is the same as SmartServerRepositoryInsertStreamLocked, except it
657
    takes no lock_tokens; i.e. it works with an unlocked (or lock-free, e.g.
658
    like pack format) repository.
659
660
    New in 1.13.
661
    """
662
663
    def do_repository_request(self, repository, resume_tokens):
664
        """StreamSink.insert_stream for a remote repository."""
665
        repository.lock_write()
666
        self.do_insert_stream_request(repository, resume_tokens)
667
668