/brz/remove-bazaar

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