~bzr-pqm/bzr/bzr.dev

4763.2.4 by John Arbash Meinel
merge bzr.2.1 in preparation for NEWS entry.
1
# Copyright (C) 2006-2010 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
23
import tempfile
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
24
import threading
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
25
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
26
from bzrlib import (
2694.5.4 by Jelmer Vernooij
Move bzrlib.util.bencode to bzrlib._bencode_py.
27
    bencode,
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
28
    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.
29
    graph,
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
30
    osutils,
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
31
    pack,
4634.124.5 by Martin Pool
Warn about inventory-delta streams when encoding for the network
32
    ui,
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
4476.3.15 by Andrew Bennetts
Partially working fallback for pre-1.17 servers.
42
from bzrlib.versionedfile import (
43
    NetworkRecordStream,
44
    record_to_fulltext_bytes,
45
    )
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
46
47
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
48
class SmartServerRepositoryRequest(SmartServerRequest):
49
    """Common base class for Repository requests."""
50
51
    def do(self, path, *args):
52
        """Execute a repository request.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
53
2692.1.10 by Andrew Bennetts
More docstring polish
54
        All Repository requests take a path to the repository as their first
55
        argument.  The repository must be at the exact path given by the
56
        client - no searching is done.
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
57
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
58
        The actual logic is delegated to self.do_repository_request.
59
2692.1.10 by Andrew Bennetts
More docstring polish
60
        :param client_path: The path for the repository as received from the
61
            client.
62
        :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).
63
        """
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
64
        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).
65
        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.
66
        # Save the repository for use with do_body.
67
        self._repository = bzrdir.open_repository()
68
        return self.do_repository_request(self._repository, *args)
69
70
    def do_repository_request(self, repository, *args):
71
        """Override to provide an implementation for a verb."""
72
        # No-op for verbs that take bodies (None as a result indicates a body
73
        # is expected)
74
        return None
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
75
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.
76
    def recreate_search(self, repository, search_bytes, discard_excess=False):
77
        """Recreate a search from its serialised form.
78
79
        :param discard_excess: If True, and the search refers to data we don't
80
            have, just silently accept that fact - the verb calling
81
            recreate_search trusts that clients will look for missing things
82
            they expected and get it from elsewhere.
83
        """
5539.2.14 by Andrew Bennetts
Don't add a new verb; instead just teach the client to fallback if it gets a BadSearch error.
84
        if search_bytes == 'everything':
85
            return graph.EverythingResult(repository), None
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
86
        lines = search_bytes.split('\n')
87
        if lines[0] == 'ancestry-of':
4070.9.14 by Andrew Bennetts
Tweaks requested by Robert's review.
88
            heads = lines[1:]
89
            search_result = graph.PendingAncestryResult(heads, repository)
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
90
            return search_result, None
91
        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.
92
            return self.recreate_search_from_recipe(repository, lines[1:],
93
                discard_excess=discard_excess)
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
94
        else:
95
            return (None, FailedSmartServerResponse(('BadSearch',)))
96
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.
97
    def recreate_search_from_recipe(self, repository, lines,
98
        discard_excess=False):
99
        """Recreate a specific revision search (vs a from-tip search).
100
101
        :param discard_excess: If True, and the search refers to data we don't
102
            have, just silently accept that fact - the verb calling
103
            recreate_search trusts that clients will look for missing things
104
            they expected and get it from elsewhere.
105
        """
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.
106
        start_keys = set(lines[0].split(' '))
107
        exclude_keys = set(lines[1].split(' '))
108
        revision_count = int(lines[2])
109
        repository.lock_read()
110
        try:
111
            search = repository.get_graph()._make_breadth_first_searcher(
112
                start_keys)
113
            while True:
114
                try:
115
                    next_revs = search.next()
116
                except StopIteration:
117
                    break
118
                search.stop_searching_any(exclude_keys.intersection(next_revs))
119
            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.
120
            if (not discard_excess and
121
                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.
122
                # we got back a different amount of data than expected, this
123
                # gets reported as NoSuchRevision, because less revisions
124
                # indicates missing revisions, and more should never happen as
125
                # the excludes list considers ghosts and ensures that ghost
126
                # filling races are not a problem.
127
                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.
128
            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.
129
        finally:
130
            repository.unlock()
131
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
132
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
133
class SmartServerRepositoryReadLocked(SmartServerRepositoryRequest):
134
    """Calls self.do_readlocked_repository_request."""
135
136
    def do_repository_request(self, repository, *args):
137
        """Read lock a repository for do_readlocked_repository_request."""
138
        repository.lock_read()
139
        try:
140
            return self.do_readlocked_repository_request(repository, *args)
141
        finally:
142
            repository.unlock()
143
144
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
145
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.
146
    """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
147
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
148
    no_extra_results = False
149
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
150
    def do_repository_request(self, repository, *revision_ids):
151
        """Get parent details for some revisions.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
152
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
153
        All the parents for revision_ids are returned. Additionally up to 64KB
154
        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.
155
        from revision_ids is returned. The verb takes a body containing the
156
        current search state, see do_body for details.
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
157
4190.1.5 by Robert Collins
Review tweaks.
158
        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.
159
        graph traversal for getting parent data are included in the result with
160
        a prefix of 'missing:'.
161
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
162
        :param repository: The repository to query in.
3172.5.8 by Robert Collins
Review feedback.
163
        :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.
164
        """
165
        self._revision_ids = revision_ids
166
        return None # Signal that we want a body.
167
168
    def do_body(self, body_bytes):
169
        """Process the current search state and perform the parent lookup.
170
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
171
        :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.
172
            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.
173
            format as Repository.get_revision_graph) which has been bz2
174
            compressed.
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
175
        """
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.
176
        repository = self._repository
177
        repository.lock_read()
178
        try:
179
            return self._do_repository_request(body_bytes)
180
        finally:
181
            repository.unlock()
182
183
    def _do_repository_request(self, body_bytes):
184
        repository = self._repository
185
        revision_ids = set(self._revision_ids)
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
186
        include_missing = 'include-missing:' in revision_ids
187
        if include_missing:
188
            revision_ids.remove('include-missing:')
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
189
        body_lines = body_bytes.split('\n')
190
        search_result, error = self.recreate_search_from_recipe(
191
            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.
192
        if error is not None:
193
            return error
194
        # TODO might be nice to start up the search again; but thats not
195
        # 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.
196
        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.
197
        # Always include the requested ids.
198
        client_seen_revs.difference_update(revision_ids)
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
199
        lines = []
200
        repo_graph = repository.get_graph()
201
        result = {}
202
        queried_revs = set()
203
        size_so_far = 0
204
        next_revs = revision_ids
205
        first_loop_done = False
206
        while next_revs:
207
            queried_revs.update(next_revs)
208
            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.
209
            current_revs = next_revs
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
210
            next_revs = set()
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
211
            for revision_id in current_revs:
212
                missing_rev = False
213
                parents = parent_map.get(revision_id)
214
                if parents is not None:
215
                    # adjust for the wire
216
                    if parents == (_mod_revision.NULL_REVISION,):
217
                        parents = ()
218
                    # prepare the next query
219
                    next_revs.update(parents)
220
                    encoded_id = revision_id
221
                else:
222
                    missing_rev = True
223
                    encoded_id = "missing:" + revision_id
224
                    parents = []
225
                if (revision_id not in client_seen_revs and
226
                    (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.
227
                    # Client does not have this revision, give it to it.
228
                    # add parents to the result
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
229
                    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.
230
                    # 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.
231
                    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.
232
            # get all the directly asked for parents, and then flesh out to
233
            # 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.
234
            # 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.
235
            # 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.
236
            if self.no_extra_results or (
237
                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.
238
                next_revs = set()
239
                break
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
240
            # don't query things we've already queried
241
            next_revs.difference_update(queried_revs)
242
            first_loop_done = True
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
        # sorting trivially puts lexographically similar revision ids together.
245
        # Compression FTW.
246
        for revision, parents in sorted(result.items()):
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
247
            lines.append(' '.join((revision, ) + tuple(parents)))
248
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.
249
        return SuccessfulSmartServerResponse(
3211.5.2 by Robert Collins
Change RemoteRepository.get_parent_map to use bz2 not gzip for compression.
250
            ('ok', ), bz2.compress('\n'.join(lines)))
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
251
252
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
253
class SmartServerRepositoryGetRevisionGraph(SmartServerRepositoryReadLocked):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
254
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
255
    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)
256
        """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.
257
258
        Deprecated as of bzr 1.4, but supported for older clients.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
259
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
260
        :param repository: The repository to query in.
261
        :param revision_id: The utf8 encoded revision_id to get a graph from.
262
        :return: A smart server response where the body contains an utf8
263
            encoded flattened list of the revision graph.
264
        """
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
265
        if not revision_id:
266
            revision_id = None
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
267
268
        lines = []
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
269
        graph = repository.get_graph()
270
        if revision_id:
271
            search_ids = [revision_id]
272
        else:
273
            search_ids = repository.all_revision_ids()
274
        search = graph._make_breadth_first_searcher(search_ids)
275
        transitive_ids = set()
276
        map(transitive_ids.update, list(search))
277
        parent_map = graph.get_parent_map(transitive_ids)
3287.6.8 by Robert Collins
Reduce code duplication as per review.
278
        revision_graph = _strip_NULL_ghosts(parent_map)
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
279
        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.
280
            # Note that we return an empty body, rather than omitting the body.
281
            # This way the client knows that it can always expect to find a body
282
            # 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.
283
            return FailedSmartServerResponse(('nosuchrevision', revision_id), '')
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
284
285
        for revision, parents in revision_graph.items():
2592.3.50 by Robert Collins
Merge bzr.dev.
286
            lines.append(' '.join((revision, ) + tuple(parents)))
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
287
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
288
        return SuccessfulSmartServerResponse(('ok', ), '\n'.join(lines))
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
289
290
4419.2.4 by Andrew Bennetts
Add Repository.get_rev_id_for_revno RPC, removes VFS calls from 'pull -r 123' case.
291
class SmartServerRepositoryGetRevIdForRevno(SmartServerRepositoryReadLocked):
292
293
    def do_readlocked_repository_request(self, repository, revno,
294
            known_pair):
295
        """Find the revid for a given revno, given a known revno/revid pair.
296
        
4419.2.16 by Andrew Bennetts
New in 1.17, not 1.16.
297
        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.
298
        """
4419.2.6 by Andrew Bennetts
Add tests for server-side logic, and fix the bugs exposed by those tests.
299
        try:
300
            found_flag, result = repository.get_rev_id_for_revno(revno, known_pair)
301
        except errors.RevisionNotPresent, err:
302
            if err.revision_id != known_pair[1]:
303
                raise AssertionError(
304
                    'get_rev_id_for_revno raised RevisionNotPresent for '
305
                    'non-initial revision: ' + err.revision_id)
306
            return FailedSmartServerResponse(
307
                ('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.
308
        if found_flag:
309
            return SuccessfulSmartServerResponse(('ok', result))
310
        else:
311
            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.
312
            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.
313
                ('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.
314
315
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
316
class SmartServerRequestHasRevision(SmartServerRepositoryRequest):
317
318
    def do_repository_request(self, repository, revision_id):
319
        """Return ok if a specific revision is in the repository at path.
320
321
        :param repository: The repository to query in.
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
322
        :param revision_id: The utf8 encoded revision_id to lookup.
323
        :return: A smart server response of ('ok', ) if the revision is
324
            present.
325
        """
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
326
        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.
327
            return SuccessfulSmartServerResponse(('yes', ))
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
328
        else:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
329
            return SuccessfulSmartServerResponse(('no', ))
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
330
331
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
332
class SmartServerRepositoryGatherStats(SmartServerRepositoryRequest):
333
334
    def do_repository_request(self, repository, revid, committers):
335
        """Return the result of repository.gather_stats().
336
337
        :param repository: The repository to query in.
338
        :param revid: utf8 encoded rev id or an empty string to indicate None
339
        :param committers: 'yes' or 'no'.
340
341
        :return: A SmartServerResponse ('ok',), a encoded body looking like
342
              committers: 1
343
              firstrev: 1234.230 0
344
              latestrev: 345.700 3600
345
              revisions: 2
346
347
              But containing only fields returned by the gather_stats() call
348
        """
349
        if revid == '':
350
            decoded_revision_id = None
351
        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.
352
            decoded_revision_id = revid
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
353
        if committers == 'yes':
354
            decoded_committers = True
355
        else:
356
            decoded_committers = None
357
        stats = repository.gather_stats(decoded_revision_id, decoded_committers)
358
359
        body = ''
360
        if stats.has_key('committers'):
361
            body += 'committers: %d\n' % stats['committers']
362
        if stats.has_key('firstrev'):
363
            body += 'firstrev: %.3f %d\n' % stats['firstrev']
364
        if stats.has_key('latestrev'):
365
             body += 'latestrev: %.3f %d\n' % stats['latestrev']
366
        if stats.has_key('revisions'):
367
            body += 'revisions: %d\n' % stats['revisions']
368
        if stats.has_key('size'):
369
            body += 'size: %d\n' % stats['size']
370
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
371
        return SuccessfulSmartServerResponse(('ok', ), body)
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
372
373
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
374
class SmartServerRepositoryIsShared(SmartServerRepositoryRequest):
375
376
    def do_repository_request(self, repository):
377
        """Return the result of repository.is_shared().
378
379
        :param repository: The repository to query in.
380
        :return: A smart server response of ('yes', ) if the repository is
381
            shared, and ('no', ) if it is not.
382
        """
383
        if repository.is_shared():
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
384
            return SuccessfulSmartServerResponse(('yes', ))
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
385
        else:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
386
            return SuccessfulSmartServerResponse(('no', ))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
387
388
389
class SmartServerRepositoryLockWrite(SmartServerRepositoryRequest):
390
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
391
    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
392
        # XXX: this probably should not have a token.
393
        if token == '':
394
            token = None
395
        try:
5200.3.3 by Robert Collins
Lock methods on ``Tree``, ``Branch`` and ``Repository`` are now
396
            token = repository.lock_write(token=token).repository_token
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
397
        except errors.LockContention, e:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
398
            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.
399
        except errors.UnlockableTransport:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
400
            return FailedSmartServerResponse(('UnlockableTransport',))
2872.5.3 by Martin Pool
Pass back LockFailed from smart server lock methods
401
        except errors.LockFailed, e:
402
            return FailedSmartServerResponse(('LockFailed',
403
                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.
404
        if token is not None:
405
            repository.leave_lock_in_place()
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
406
        repository.unlock()
407
        if token is None:
408
            token = ''
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
409
        return SuccessfulSmartServerResponse(('ok', token))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
410
411
4060.1.5 by Robert Collins
Verb change name requested by Andrew.
412
class SmartServerRepositoryGetStream(SmartServerRepositoryRequest):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
413
414
    def do_repository_request(self, repository, to_network_name):
415
        """Get a stream for inserting into a to_format repository.
416
5539.2.14 by Andrew Bennetts
Don't add a new verb; instead just teach the client to fallback if it gets a BadSearch error.
417
        The request body is 'search_bytes', a description of the revisions
418
        being requested.
419
420
        In 2.3 this verb added support for search_bytes == 'everything'.  Older
421
        implementations will respond with a BadSearch error, and clients should
422
        catch this and fallback appropriately.
423
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
424
        :param repository: The repository to stream from.
425
        :param to_network_name: The network name of the format of the target
426
            repository.
427
        """
428
        self._to_format = network_format_registry.get(to_network_name)
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
429
        if self._should_fake_unknown():
430
            return FailedSmartServerResponse(
431
                ('UnknownMethod', 'Repository.get_stream'))
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
432
        return None # Signal that we want a body.
433
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
434
    def _should_fake_unknown(self):
4476.3.80 by Andrew Bennetts
Comment/docstring tweaks prompted by review.
435
        """Return True if we should return UnknownMethod to the client.
436
        
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
437
        This is a workaround for bugs in pre-1.19 clients that claim to
438
        support receiving streams of CHK repositories.  The pre-1.19 client
4476.3.80 by Andrew Bennetts
Comment/docstring tweaks prompted by review.
439
        expects inventory records to be serialized in the format defined by
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
440
        to_network_name, but in pre-1.19 (at least) that format definition
4476.3.80 by Andrew Bennetts
Comment/docstring tweaks prompted by review.
441
        tries to use the xml5 serializer, which does not correctly handle
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
442
        rich-roots.  After 1.19 the client can also accept inventory-deltas
4476.3.80 by Andrew Bennetts
Comment/docstring tweaks prompted by review.
443
        (which avoids this issue), and those clients will use the
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
444
        Repository.get_stream_1.19 verb instead of this one.
4476.3.80 by Andrew Bennetts
Comment/docstring tweaks prompted by review.
445
        So: if this repository is CHK, and the to_format doesn't match,
446
        we should just fake an UnknownSmartMethod error so that the client
447
        will fallback to VFS, rather than sending it a stream we know it
448
        cannot handle.
449
        """
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
450
        from_format = self._repository._format
451
        to_format = self._to_format
452
        if not from_format.supports_chks:
453
            # Source not CHK: that's ok
454
            return False
455
        if (to_format.supports_chks and
456
            from_format.repository_class is to_format.repository_class and
457
            from_format._serializer == to_format._serializer):
458
            # Source is CHK, but target matches: that's ok
459
            # (e.g. 2a->2a, or CHK2->2a)
460
            return False
461
        # Source is CHK, and target is not CHK or incompatible CHK.  We can't
462
        # generate a compatible stream.
463
        return True
464
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
465
    def do_body(self, body_bytes):
466
        repository = self._repository
467
        repository.lock_read()
468
        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.
469
            search_result, error = self.recreate_search(repository, body_bytes,
470
                discard_excess=True)
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
471
            if error is not None:
472
                repository.unlock()
473
                return error
474
            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.
475
            stream = source.get_stream(search_result)
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
476
        except Exception:
477
            exc_info = sys.exc_info()
478
            try:
479
                # On non-error, unlocking is done by the body stream handler.
480
                repository.unlock()
481
            finally:
482
                raise exc_info[0], exc_info[1], exc_info[2]
483
        return SuccessfulSmartServerResponse(('ok',),
484
            body_stream=self.body_stream(stream, repository))
485
486
    def body_stream(self, stream, repository):
487
        byte_stream = _stream_to_byte_stream(stream, repository._format)
488
        try:
489
            for bytes in byte_stream:
490
                yield bytes
491
        except errors.RevisionNotPresent, e:
492
            # This shouldn't be able to happen, but as we don't buffer
493
            # everything it can in theory happen.
494
            repository.unlock()
495
            yield FailedSmartServerResponse(('NoSuchRevision', e.revision_id))
496
        else:
497
            repository.unlock()
498
499
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
500
class SmartServerRepositoryGetStream_1_19(SmartServerRepositoryGetStream):
5539.2.14 by Andrew Bennetts
Don't add a new verb; instead just teach the client to fallback if it gets a BadSearch error.
501
    """The same as Repository.get_stream, but will return stream CHK formats to
502
    clients.
503
504
    See SmartServerRepositoryGetStream._should_fake_unknown.
505
    
506
    New in 1.19.
507
    """
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
508
509
    def _should_fake_unknown(self):
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
510
        """Returns False; we don't need to workaround bugs in 1.19+ clients."""
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
511
        return False
512
513
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
514
def _stream_to_byte_stream(stream, src_format):
515
    """Convert a record stream to a self delimited byte stream."""
516
    pack_writer = pack.ContainerSerialiser()
517
    yield pack_writer.begin()
518
    yield pack_writer.bytes_record(src_format.network_name(), '')
519
    for substream_type, substream in stream:
520
        for record in substream:
521
            if record.storage_kind in ('chunked', 'fulltext'):
522
                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.
523
            elif record.storage_kind == 'absent':
524
                raise ValueError("Absent factory for %s" % (record.key,))
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
525
            else:
526
                serialised = record.get_bytes_as(record.storage_kind)
527
            if serialised:
528
                # Some streams embed the whole stream into the wire
529
                # representation of the first record, which means that
530
                # later records have no wire representation: we skip them.
531
                yield pack_writer.bytes_record(serialised, [(substream_type,)])
532
    yield pack_writer.end()
533
534
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
535
class _ByteStreamDecoder(object):
536
    """Helper for _byte_stream_to_stream.
537
4634.19.2 by Robert Collins
Review feedback.
538
    The expected usage of this class is via the function _byte_stream_to_stream
539
    which creates a _ByteStreamDecoder, pops off the stream format and then
540
    yields the output of record_stream(), the main entry point to
541
    _ByteStreamDecoder.
542
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
543
    Broadly this class has to unwrap two layers of iterators:
544
    (type, substream)
545
    (substream details)
546
547
    This is complicated by wishing to return type, iterator_for_type, but
548
    getting the data for iterator_for_type when we find out type: we can't
549
    simply pass a generator down to the NetworkRecordStream parser, instead
550
    we have a little local state to seed each NetworkRecordStream instance,
551
    and gather the type that we'll be yielding.
552
553
    :ivar byte_stream: The byte stream being decoded.
554
    :ivar stream_decoder: A pack parser used to decode the bytestream
555
    :ivar current_type: The current type, used to join adjacent records of the
556
        same type into a single stream.
557
    :ivar first_bytes: The first bytes to give the next NetworkRecordStream.
558
    """
559
5195.3.23 by Parth Malwankar
moved progress bar logic to SourceStream.
560
    def __init__(self, byte_stream, record_counter):
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
561
        """Create a _ByteStreamDecoder."""
562
        self.stream_decoder = pack.ContainerPushParser()
563
        self.current_type = None
564
        self.first_bytes = None
565
        self.byte_stream = byte_stream
5195.3.27 by Parth Malwankar
code cleanup and comments.
566
        self._record_counter = record_counter
5195.3.23 by Parth Malwankar
moved progress bar logic to SourceStream.
567
        self.key_count = 0
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
568
569
    def iter_stream_decoder(self):
570
        """Iterate the contents of the pack from stream_decoder."""
571
        # dequeue pending items
572
        for record in self.stream_decoder.read_pending_records():
573
            yield record
574
        # Pull bytes of the wire, decode them to records, yield those records.
575
        for bytes in self.byte_stream:
576
            self.stream_decoder.accept_bytes(bytes)
577
            for record in self.stream_decoder.read_pending_records():
578
                yield record
579
580
    def iter_substream_bytes(self):
581
        if self.first_bytes is not None:
582
            yield self.first_bytes
583
            # If we run out of pack records, single the outer layer to stop.
584
            self.first_bytes = None
585
        for record in self.iter_pack_records:
586
            record_names, record_bytes = record
587
            record_name, = record_names
588
            substream_type = record_name[0]
589
            if substream_type != self.current_type:
590
                # end of a substream, seed the next substream.
591
                self.current_type = substream_type
592
                self.first_bytes = record_bytes
593
                return
594
            yield record_bytes
595
596
    def record_stream(self):
597
        """Yield substream_type, substream from the byte stream."""
5195.3.27 by Parth Malwankar
code cleanup and comments.
598
        def wrap_and_count(pb, rc, substream):
599
            """Yield records from stream while showing progress."""
600
            counter = 0
601
            if rc:
602
                if self.current_type != 'revisions' and self.key_count != 0:
603
                    # As we know the number of revisions now (in self.key_count)
604
                    # we can setup and use record_counter (rc).
605
                    if not rc.is_initialized():
606
                        rc.setup(self.key_count, self.key_count)
607
            for record in substream.read():
608
                if rc:
609
                    if rc.is_initialized() and counter == rc.STEP:
610
                        rc.increment(counter)
611
                        pb.update('Estimate', rc.current, rc.max)
612
                        counter = 0
613
                    if self.current_type == 'revisions':
614
                        # Total records is proportional to number of revs
615
                        # to fetch. With remote, we used self.key_count to
616
                        # track the number of revs. Once we have the revs
617
                        # counts in self.key_count, the progress bar changes
618
                        # from 'Estimating..' to 'Estimate' above.
619
                        self.key_count += 1
620
                        if counter == rc.STEP:
621
                            pb.update('Estimating..', self.key_count)
622
                            counter = 0
623
                counter += 1
624
                yield record
625
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
626
        self.seed_state()
5195.3.23 by Parth Malwankar
moved progress bar logic to SourceStream.
627
        pb = ui.ui_factory.nested_progress_bar()
5195.3.27 by Parth Malwankar
code cleanup and comments.
628
        rc = self._record_counter
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
629
        # Make and consume sub generators, one per substream type:
630
        while self.first_bytes is not None:
631
            substream = NetworkRecordStream(self.iter_substream_bytes())
632
            # after substream is fully consumed, self.current_type is set to
633
            # the next type, and self.first_bytes is set to the matching bytes.
5195.3.27 by Parth Malwankar
code cleanup and comments.
634
            yield self.current_type, wrap_and_count(pb, rc, substream)
635
        if rc:
636
            pb.update('Done', rc.max, rc.max)
5195.3.23 by Parth Malwankar
moved progress bar logic to SourceStream.
637
        pb.finished()
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
638
639
    def seed_state(self):
640
        """Prepare the _ByteStreamDecoder to decode from the pack stream."""
641
        # Set a single generator we can use to get data from the pack stream.
642
        self.iter_pack_records = self.iter_stream_decoder()
643
        # Seed the very first subiterator with content; after this each one
644
        # seeds the next.
645
        list(self.iter_substream_bytes())
646
647
5195.3.23 by Parth Malwankar
moved progress bar logic to SourceStream.
648
def _byte_stream_to_stream(byte_stream, record_counter=None):
4060.1.5 by Robert Collins
Verb change name requested by Andrew.
649
    """Convert a byte stream into a format and a stream.
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
650
651
    :param byte_stream: A bytes iterator, as output by _stream_to_byte_stream.
652
    :return: (RepositoryFormat, stream_generator)
653
    """
5195.3.23 by Parth Malwankar
moved progress bar logic to SourceStream.
654
    decoder = _ByteStreamDecoder(byte_stream, record_counter)
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
655
    for bytes in byte_stream:
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
656
        decoder.stream_decoder.accept_bytes(bytes)
657
        for record in decoder.stream_decoder.read_pending_records(max=1):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
658
            record_names, src_format_name = record
659
            src_format = network_format_registry.get(src_format_name)
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
660
            return src_format, decoder.record_stream()
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
661
662
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
663
class SmartServerRepositoryUnlock(SmartServerRepositoryRequest):
664
665
    def do_repository_request(self, repository, token):
666
        try:
667
            repository.lock_write(token=token)
668
        except errors.TokenMismatch, e:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
669
            return FailedSmartServerResponse(('TokenMismatch',))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
670
        repository.dont_leave_lock_in_place()
671
        repository.unlock()
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
672
        return SuccessfulSmartServerResponse(('ok',))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
673
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
674
4017.3.4 by Robert Collins
Create a verb for Repository.set_make_working_trees.
675
class SmartServerRepositorySetMakeWorkingTrees(SmartServerRepositoryRequest):
676
677
    def do_repository_request(self, repository, str_bool_new_value):
678
        if str_bool_new_value == 'True':
679
            new_value = True
680
        else:
681
            new_value = False
682
        repository.set_make_working_trees(new_value)
683
        return SuccessfulSmartServerResponse(('ok',))
684
685
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
686
class SmartServerRepositoryTarball(SmartServerRepositoryRequest):
2018.18.11 by Martin Pool
merge hpss changes
687
    """Get the raw repository files as a tarball.
688
689
    The returned tarball contains a .bzr control directory which in turn
690
    contains a repository.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
691
692
    This takes one parameter, compression, which currently must be
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
693
    "", "gz", or "bz2".
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
694
695
    This is used to implement the Repository.copy_content_into operation.
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
696
    """
697
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
698
    def do_repository_request(self, repository, compression):
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
699
        tmp_dirname, tmp_repo = self._copy_to_tempdir(repository)
2018.18.5 by Martin Pool
Repository.tarball locks repository while running for consistency
700
        try:
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
701
            controldir_name = tmp_dirname + '/.bzr'
702
            return self._tarfile_response(controldir_name, compression)
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
703
        finally:
704
            osutils.rmtree(tmp_dirname)
705
706
    def _copy_to_tempdir(self, from_repo):
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
707
        tmp_dirname = osutils.mkdtemp(prefix='tmpbzrclone')
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
708
        tmp_bzrdir = from_repo.bzrdir._format.initialize(tmp_dirname)
709
        tmp_repo = from_repo._format.initialize(tmp_bzrdir)
710
        from_repo.copy_content_into(tmp_repo)
711
        return tmp_dirname, tmp_repo
712
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
713
    def _tarfile_response(self, tmp_dirname, compression):
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
714
        temp = tempfile.NamedTemporaryFile()
715
        try:
2557.1.1 by Martin Pool
[BUG 119330] Fix tempfile permissions error in smart server tar bundling (under windows) (Martin_)
716
            self._tarball_of_dir(tmp_dirname, compression, temp.file)
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
717
            # all finished; write the tempfile out to the network
718
            temp.seek(0)
2420.2.2 by Andrew Bennetts
Merge tarball branch that's already with PQM, resolving conflicts.
719
            return SuccessfulSmartServerResponse(('ok',), temp.read())
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
720
            # FIXME: Don't read the whole thing into memory here; rather stream
721
            # it out from the file onto the network. mbp 20070411
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
722
        finally:
723
            temp.close()
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
724
2557.1.1 by Martin Pool
[BUG 119330] Fix tempfile permissions error in smart server tar bundling (under windows) (Martin_)
725
    def _tarball_of_dir(self, dirname, compression, ofile):
5017.2.4 by Martin Pool
Move or remove some unconditionally loaded code
726
        import tarfile
2571.2.2 by Ian Clatworthy
use basename as poolie recommended
727
        filename = os.path.basename(ofile.name)
728
        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
729
            mode='w|' + compression)
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
730
        try:
731
            # The tarball module only accepts ascii names, and (i guess)
732
            # packs them with their 8bit names.  We know all the files
733
            # within the repository have ASCII names so the should be safe
734
            # to pack in.
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
735
            dirname = dirname.encode(sys.getfilesystemencoding())
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
736
            # python's tarball module includes the whole path by default so
737
            # override it
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
738
            if not dirname.endswith('.bzr'):
739
                raise ValueError(dirname)
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
740
            tarball.add(dirname, '.bzr') # recursive by default
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
741
        finally:
742
            tarball.close()
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
743
744
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
745
class SmartServerRepositoryInsertStreamLocked(SmartServerRepositoryRequest):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
746
    """Insert a record stream from a RemoteSink into a repository.
747
748
    This gets bytes pushed to it by the network infrastructure and turns that
749
    into a bytes iterator using a thread. That is then processed by
750
    _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.
751
752
    New in 1.14.
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
753
    """
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
754
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
755
    def do_repository_request(self, repository, resume_tokens, lock_token):
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
756
        """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.
757
        repository.lock_write(token=lock_token)
758
        self.do_insert_stream_request(repository, resume_tokens)
759
760
    def do_insert_stream_request(self, repository, resume_tokens):
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
761
        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.
762
        self.tokens = tokens
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
763
        self.repository = repository
764
        self.queue = Queue.Queue()
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
765
        self.insert_thread = threading.Thread(target=self._inserter_thread)
766
        self.insert_thread.start()
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
767
768
    def do_chunk(self, body_stream_chunk):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
769
        self.queue.put(body_stream_chunk)
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
770
771
    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.
772
        try:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
773
            src_format, stream = _byte_stream_to_stream(
774
                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.
775
            self.insert_result = self.repository._get_sink().insert_stream(
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
776
                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.
777
            self.insert_ok = True
778
        except:
779
            self.insert_exception = sys.exc_info()
780
            self.insert_ok = False
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
781
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
782
    def blocking_byte_stream(self):
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
783
        while True:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
784
            bytes = self.queue.get()
785
            if bytes is StopIteration:
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
786
                return
787
            else:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
788
                yield bytes
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
789
790
    def do_end(self):
791
        self.queue.put(StopIteration)
792
        if self.insert_thread is not None:
793
            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.
794
        if not self.insert_ok:
795
            exc_info = self.insert_exception
796
            raise exc_info[0], exc_info[1], exc_info[2]
797
        write_group_tokens, missing_keys = self.insert_result
798
        if write_group_tokens or missing_keys:
799
            # bzip needed? missing keys should typically be a small set.
800
            # Should this be a streaming body response ?
801
            missing_keys = sorted(missing_keys)
802
            bytes = bencode.bencode((write_group_tokens, missing_keys))
803
            self.repository.unlock()
804
            return SuccessfulSmartServerResponse(('missing-basis', bytes))
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
805
        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.
806
            self.repository.unlock()
807
            return SuccessfulSmartServerResponse(('ok', ))
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
808
809
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
810
class SmartServerRepositoryInsertStream_1_19(SmartServerRepositoryInsertStreamLocked):
4476.3.15 by Andrew Bennetts
Partially working fallback for pre-1.17 servers.
811
    """Insert a record stream from a RemoteSink into a repository.
812
813
    Same as SmartServerRepositoryInsertStreamLocked, except:
814
     - the lock token argument is optional
815
     - servers that implement this verb accept 'inventory-delta' records in the
816
       stream.
817
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
818
    New in 1.19.
4476.3.15 by Andrew Bennetts
Partially working fallback for pre-1.17 servers.
819
    """
820
821
    def do_repository_request(self, repository, resume_tokens, lock_token=None):
822
        """StreamSink.insert_stream for a remote repository."""
823
        SmartServerRepositoryInsertStreamLocked.do_repository_request(
824
            self, repository, resume_tokens, lock_token)
825
826
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
827
class SmartServerRepositoryInsertStream(SmartServerRepositoryInsertStreamLocked):
828
    """Insert a record stream from a RemoteSink into an unlocked repository.
829
830
    This is the same as SmartServerRepositoryInsertStreamLocked, except it
831
    takes no lock_tokens; i.e. it works with an unlocked (or lock-free, e.g.
832
    like pack format) repository.
833
834
    New in 1.13.
835
    """
836
837
    def do_repository_request(self, repository, resume_tokens):
838
        """StreamSink.insert_stream for a remote repository."""
839
        repository.lock_write()
840
        self.do_insert_stream_request(repository, resume_tokens)
841
842