/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,
6118.2.9 by John Arbash Meinel
Add some tests for how the estimator works.
29
    estimate_compressed_size,
4070.9.2 by Andrew Bennetts
Rough prototype of allowing a SearchResult to be passed to fetch, and using that to improve network conversations.
30
    graph,
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
31
    osutils,
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
32
    pack,
6118.2.1 by John Arbash Meinel
Refactor the Remote get_parent_map loop, and change how we compute how much prefetch to do.
33
    trace,
4634.124.5 by Martin Pool
Warn about inventory-delta streams when encoding for the network
34
    ui,
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
35
    )
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
36
from bzrlib.bzrdir import BzrDir
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
37
from bzrlib.smart.request import (
38
    FailedSmartServerResponse,
39
    SmartServerRequest,
40
    SuccessfulSmartServerResponse,
41
    )
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.
42
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.
43
from bzrlib import revision as _mod_revision
4476.3.15 by Andrew Bennetts
Partially working fallback for pre-1.17 servers.
44
from bzrlib.versionedfile import (
45
    NetworkRecordStream,
46
    record_to_fulltext_bytes,
47
    )
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
48
49
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
50
class SmartServerRepositoryRequest(SmartServerRequest):
51
    """Common base class for Repository requests."""
52
53
    def do(self, path, *args):
54
        """Execute a repository request.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
55
2692.1.10 by Andrew Bennetts
More docstring polish
56
        All Repository requests take a path to the repository as their first
57
        argument.  The repository must be at the exact path given by the
58
        client - no searching is done.
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
59
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
60
        The actual logic is delegated to self.do_repository_request.
61
2692.1.10 by Andrew Bennetts
More docstring polish
62
        :param client_path: The path for the repository as received from the
63
            client.
64
        :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).
65
        """
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
66
        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).
67
        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.
68
        # Save the repository for use with do_body.
69
        self._repository = bzrdir.open_repository()
70
        return self.do_repository_request(self._repository, *args)
71
72
    def do_repository_request(self, repository, *args):
73
        """Override to provide an implementation for a verb."""
74
        # No-op for verbs that take bodies (None as a result indicates a body
75
        # is expected)
76
        return None
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
77
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.
78
    def recreate_search(self, repository, search_bytes, discard_excess=False):
79
        """Recreate a search from its serialised form.
80
81
        :param discard_excess: If True, and the search refers to data we don't
82
            have, just silently accept that fact - the verb calling
83
            recreate_search trusts that clients will look for missing things
84
            they expected and get it from elsewhere.
85
        """
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.
86
        if search_bytes == 'everything':
87
            return graph.EverythingResult(repository), None
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
88
        lines = search_bytes.split('\n')
89
        if lines[0] == 'ancestry-of':
4070.9.14 by Andrew Bennetts
Tweaks requested by Robert's review.
90
            heads = lines[1:]
91
            search_result = graph.PendingAncestryResult(heads, repository)
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
92
            return search_result, None
93
        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.
94
            return self.recreate_search_from_recipe(repository, lines[1:],
95
                discard_excess=discard_excess)
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
96
        else:
97
            return (None, FailedSmartServerResponse(('BadSearch',)))
98
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.
99
    def recreate_search_from_recipe(self, repository, lines,
100
        discard_excess=False):
101
        """Recreate a specific revision search (vs a from-tip search).
102
103
        :param discard_excess: If True, and the search refers to data we don't
104
            have, just silently accept that fact - the verb calling
105
            recreate_search trusts that clients will look for missing things
106
            they expected and get it from elsewhere.
107
        """
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.
108
        start_keys = set(lines[0].split(' '))
109
        exclude_keys = set(lines[1].split(' '))
110
        revision_count = int(lines[2])
111
        repository.lock_read()
112
        try:
113
            search = repository.get_graph()._make_breadth_first_searcher(
114
                start_keys)
115
            while True:
116
                try:
117
                    next_revs = search.next()
118
                except StopIteration:
119
                    break
120
                search.stop_searching_any(exclude_keys.intersection(next_revs))
121
            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.
122
            if (not discard_excess and
123
                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.
124
                # we got back a different amount of data than expected, this
125
                # gets reported as NoSuchRevision, because less revisions
126
                # indicates missing revisions, and more should never happen as
127
                # the excludes list considers ghosts and ensures that ghost
128
                # filling races are not a problem.
129
                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.
130
            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.
131
        finally:
132
            repository.unlock()
133
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
134
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
135
class SmartServerRepositoryReadLocked(SmartServerRepositoryRequest):
136
    """Calls self.do_readlocked_repository_request."""
137
138
    def do_repository_request(self, repository, *args):
139
        """Read lock a repository for do_readlocked_repository_request."""
140
        repository.lock_read()
141
        try:
142
            return self.do_readlocked_repository_request(repository, *args)
143
        finally:
144
            repository.unlock()
145
6280.4.2 by Jelmer Vernooij
Provide server side of Repository.break_lock HPSS call.
146
147
class SmartServerRepositoryBreakLock(SmartServerRepositoryRequest):
148
    """Break a repository lock."""
149
150
    def do_repository_request(self, repository):
151
        repository.break_lock()
152
        return SuccessfulSmartServerResponse(('ok', ))
153
154
6118.2.4 by John Arbash Meinel
debugging. Use lsprof to determine where we are spending our time.
155
_lsprof_count = 0
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
156
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
157
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.
158
    """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
159
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
160
    no_extra_results = False
161
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
162
    def do_repository_request(self, repository, *revision_ids):
163
        """Get parent details for some revisions.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
164
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
165
        All the parents for revision_ids are returned. Additionally up to 64KB
166
        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.
167
        from revision_ids is returned. The verb takes a body containing the
168
        current search state, see do_body for details.
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
169
4190.1.5 by Robert Collins
Review tweaks.
170
        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.
171
        graph traversal for getting parent data are included in the result with
172
        a prefix of 'missing:'.
173
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
174
        :param repository: The repository to query in.
3172.5.8 by Robert Collins
Review feedback.
175
        :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.
176
        """
177
        self._revision_ids = revision_ids
178
        return None # Signal that we want a body.
179
180
    def do_body(self, body_bytes):
181
        """Process the current search state and perform the parent lookup.
182
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
183
        :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.
184
            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.
185
            format as Repository.get_revision_graph) which has been bz2
186
            compressed.
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
187
        """
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.
188
        repository = self._repository
189
        repository.lock_read()
190
        try:
191
            return self._do_repository_request(body_bytes)
192
        finally:
193
            repository.unlock()
194
6118.2.1 by John Arbash Meinel
Refactor the Remote get_parent_map loop, and change how we compute how much prefetch to do.
195
    def _expand_requested_revs(self, repo_graph, revision_ids, client_seen_revs,
196
                               include_missing, max_size=65536):
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
197
        result = {}
198
        queried_revs = set()
6118.2.9 by John Arbash Meinel
Add some tests for how the estimator works.
199
        estimator = estimate_compressed_size.ZLibEstimator(max_size)
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
200
        next_revs = revision_ids
201
        first_loop_done = False
202
        while next_revs:
203
            queried_revs.update(next_revs)
204
            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.
205
            current_revs = next_revs
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
206
            next_revs = set()
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
207
            for revision_id in current_revs:
208
                missing_rev = False
209
                parents = parent_map.get(revision_id)
210
                if parents is not None:
211
                    # adjust for the wire
212
                    if parents == (_mod_revision.NULL_REVISION,):
213
                        parents = ()
214
                    # prepare the next query
215
                    next_revs.update(parents)
216
                    encoded_id = revision_id
217
                else:
218
                    missing_rev = True
219
                    encoded_id = "missing:" + revision_id
220
                    parents = []
221
                if (revision_id not in client_seen_revs and
222
                    (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.
223
                    # Client does not have this revision, give it to it.
224
                    # add parents to the result
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
225
                    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.
226
                    # Approximate the serialized cost of this revision_id.
6118.2.1 by John Arbash Meinel
Refactor the Remote get_parent_map loop, and change how we compute how much prefetch to do.
227
                    line = '%s %s\n' % (encoded_id, ' '.join(parents))
6118.2.3 by John Arbash Meinel
An 'entropy' computation.
228
                    estimator.add_content(line)
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.
229
            # get all the directly asked for parents, and then flesh out to
230
            # 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.
231
            # 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.
232
            # estimated compression ratio taken from bzr.dev itself.
6118.2.3 by John Arbash Meinel
An 'entropy' computation.
233
            if self.no_extra_results or (first_loop_done and estimator.full()):
6118.2.6 by John Arbash Meinel
Updates to ZLibEstimator.
234
                trace.mutter('size: %d, z_size: %d'
6118.2.3 by John Arbash Meinel
An 'entropy' computation.
235
                             % (estimator._uncompressed_size_added,
6118.2.6 by John Arbash Meinel
Updates to ZLibEstimator.
236
                                estimator._compressed_size_added))
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.
237
                next_revs = set()
238
                break
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
239
            # don't query things we've already queried
5609.56.1 by John Arbash Meinel
Repository.get_parent_map was doing an inefficient set op.
240
            next_revs = next_revs.difference(queried_revs)
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
241
            first_loop_done = True
6118.2.1 by John Arbash Meinel
Refactor the Remote get_parent_map loop, and change how we compute how much prefetch to do.
242
        return result
243
244
    def _do_repository_request(self, body_bytes):
245
        repository = self._repository
246
        revision_ids = set(self._revision_ids)
247
        include_missing = 'include-missing:' in revision_ids
248
        if include_missing:
249
            revision_ids.remove('include-missing:')
250
        body_lines = body_bytes.split('\n')
251
        search_result, error = self.recreate_search_from_recipe(
252
            repository, body_lines)
253
        if error is not None:
254
            return error
255
        # TODO might be nice to start up the search again; but thats not
256
        # written or tested yet.
257
        client_seen_revs = set(search_result.get_keys())
258
        # Always include the requested ids.
259
        client_seen_revs.difference_update(revision_ids)
260
261
        repo_graph = repository.get_graph()
262
        result = self._expand_requested_revs(repo_graph, revision_ids,
263
                                             client_seen_revs, include_missing)
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
264
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.
265
        # sorting trivially puts lexographically similar revision ids together.
266
        # Compression FTW.
6118.2.1 by John Arbash Meinel
Refactor the Remote get_parent_map loop, and change how we compute how much prefetch to do.
267
        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.
268
        for revision, parents in sorted(result.items()):
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
269
            lines.append(' '.join((revision, ) + tuple(parents)))
270
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.
271
        return SuccessfulSmartServerResponse(
3211.5.2 by Robert Collins
Change RemoteRepository.get_parent_map to use bz2 not gzip for compression.
272
            ('ok', ), bz2.compress('\n'.join(lines)))
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
273
274
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
275
class SmartServerRepositoryGetRevisionGraph(SmartServerRepositoryReadLocked):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
276
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
277
    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)
278
        """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.
279
280
        Deprecated as of bzr 1.4, but supported for older clients.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
281
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
282
        :param repository: The repository to query in.
283
        :param revision_id: The utf8 encoded revision_id to get a graph from.
284
        :return: A smart server response where the body contains an utf8
285
            encoded flattened list of the revision graph.
286
        """
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
287
        if not revision_id:
288
            revision_id = None
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
289
290
        lines = []
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
291
        graph = repository.get_graph()
292
        if revision_id:
293
            search_ids = [revision_id]
294
        else:
295
            search_ids = repository.all_revision_ids()
296
        search = graph._make_breadth_first_searcher(search_ids)
297
        transitive_ids = set()
298
        map(transitive_ids.update, list(search))
299
        parent_map = graph.get_parent_map(transitive_ids)
3287.6.8 by Robert Collins
Reduce code duplication as per review.
300
        revision_graph = _strip_NULL_ghosts(parent_map)
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
301
        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.
302
            # Note that we return an empty body, rather than omitting the body.
303
            # This way the client knows that it can always expect to find a body
304
            # 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.
305
            return FailedSmartServerResponse(('nosuchrevision', revision_id), '')
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
306
307
        for revision, parents in revision_graph.items():
2592.3.50 by Robert Collins
Merge bzr.dev.
308
            lines.append(' '.join((revision, ) + tuple(parents)))
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
309
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
310
        return SuccessfulSmartServerResponse(('ok', ), '\n'.join(lines))
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
311
312
4419.2.4 by Andrew Bennetts
Add Repository.get_rev_id_for_revno RPC, removes VFS calls from 'pull -r 123' case.
313
class SmartServerRepositoryGetRevIdForRevno(SmartServerRepositoryReadLocked):
314
315
    def do_readlocked_repository_request(self, repository, revno,
316
            known_pair):
317
        """Find the revid for a given revno, given a known revno/revid pair.
318
        
4419.2.16 by Andrew Bennetts
New in 1.17, not 1.16.
319
        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.
320
        """
4419.2.6 by Andrew Bennetts
Add tests for server-side logic, and fix the bugs exposed by those tests.
321
        try:
322
            found_flag, result = repository.get_rev_id_for_revno(revno, known_pair)
323
        except errors.RevisionNotPresent, err:
324
            if err.revision_id != known_pair[1]:
325
                raise AssertionError(
326
                    'get_rev_id_for_revno raised RevisionNotPresent for '
327
                    'non-initial revision: ' + err.revision_id)
328
            return FailedSmartServerResponse(
329
                ('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.
330
        if found_flag:
331
            return SuccessfulSmartServerResponse(('ok', result))
332
        else:
333
            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.
334
            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.
335
                ('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.
336
337
6280.5.2 by Jelmer Vernooij
New HPSS call VersionedFileRepository.get_serializer_format.
338
class SmartServerRepositoryGetSerializerFormat(SmartServerRepositoryRequest):
339
340
    def do_repository_request(self, repository):
341
        """Return the serializer format for this repository.
342
343
        New in 2.5.0.
344
345
        :param repository: The repository to query
346
        :return: A smart server response ('ok', FORMAT)
347
        """
348
        serializer = repository.get_serializer_format()
349
        return SuccessfulSmartServerResponse(('ok', serializer))
350
351
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
352
class SmartServerRequestHasRevision(SmartServerRepositoryRequest):
353
354
    def do_repository_request(self, repository, revision_id):
355
        """Return ok if a specific revision is in the repository at path.
356
357
        :param repository: The repository to query in.
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
358
        :param revision_id: The utf8 encoded revision_id to lookup.
6265.2.1 by Jelmer Vernooij
fix docstring
359
        :return: A smart server response of ('yes', ) if the revision is
360
            present. ('no', ) if it is missing.
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
361
        """
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
362
        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.
363
            return SuccessfulSmartServerResponse(('yes', ))
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
364
        else:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
365
            return SuccessfulSmartServerResponse(('no', ))
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
366
367
6265.1.1 by Jelmer Vernooij
Add new HPSS call ``Repository.has_signature_for_revision_id``.
368
class SmartServerRequestHasSignatureForRevisionId(
369
        SmartServerRepositoryRequest):
370
371
    def do_repository_request(self, repository, revision_id):
372
        """Return ok if a signature is present for a revision.
373
6265.1.2 by Jelmer Vernooij
Document when Repository.has_signature_for_revision_id was introduced.
374
        Introduced in bzr 2.5.0.
375
6265.1.1 by Jelmer Vernooij
Add new HPSS call ``Repository.has_signature_for_revision_id``.
376
        :param repository: The repository to query in.
377
        :param revision_id: The utf8 encoded revision_id to lookup.
6265.2.1 by Jelmer Vernooij
fix docstring
378
        :return: A smart server response of ('yes', ) if a
379
            signature for the revision is present,
380
            ('no', ) if it is missing.
6265.1.1 by Jelmer Vernooij
Add new HPSS call ``Repository.has_signature_for_revision_id``.
381
        """
382
        try:
383
            if repository.has_signature_for_revision_id(revision_id):
384
                return SuccessfulSmartServerResponse(('yes', ))
385
            else:
386
                return SuccessfulSmartServerResponse(('no', ))
387
        except errors.NoSuchRevision:
388
            return FailedSmartServerResponse(
6265.1.5 by Jelmer Vernooij
Fix capitalization - NoSuchRevision is for branches.
389
                ('nosuchrevision', revision_id))
6265.1.1 by Jelmer Vernooij
Add new HPSS call ``Repository.has_signature_for_revision_id``.
390
391
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
392
class SmartServerRepositoryGatherStats(SmartServerRepositoryRequest):
393
394
    def do_repository_request(self, repository, revid, committers):
395
        """Return the result of repository.gather_stats().
396
397
        :param repository: The repository to query in.
398
        :param revid: utf8 encoded rev id or an empty string to indicate None
399
        :param committers: 'yes' or 'no'.
400
401
        :return: A SmartServerResponse ('ok',), a encoded body looking like
402
              committers: 1
403
              firstrev: 1234.230 0
404
              latestrev: 345.700 3600
405
              revisions: 2
406
407
              But containing only fields returned by the gather_stats() call
408
        """
409
        if revid == '':
410
            decoded_revision_id = None
411
        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.
412
            decoded_revision_id = revid
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
413
        if committers == 'yes':
414
            decoded_committers = True
415
        else:
416
            decoded_committers = None
6291.1.1 by Jelmer Vernooij
Cope with missing revision ids being specified to
417
        try:
418
            stats = repository.gather_stats(decoded_revision_id,
419
                decoded_committers)
420
        except errors.NoSuchRevision:
421
            return FailedSmartServerResponse(('nosuchrevision', revid))
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
422
423
        body = ''
424
        if stats.has_key('committers'):
425
            body += 'committers: %d\n' % stats['committers']
426
        if stats.has_key('firstrev'):
427
            body += 'firstrev: %.3f %d\n' % stats['firstrev']
428
        if stats.has_key('latestrev'):
429
             body += 'latestrev: %.3f %d\n' % stats['latestrev']
430
        if stats.has_key('revisions'):
431
            body += 'revisions: %d\n' % stats['revisions']
432
        if stats.has_key('size'):
433
            body += 'size: %d\n' % stats['size']
434
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
435
        return SuccessfulSmartServerResponse(('ok', ), body)
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
436
437
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
438
class SmartServerRepositoryIsShared(SmartServerRepositoryRequest):
439
440
    def do_repository_request(self, repository):
441
        """Return the result of repository.is_shared().
442
443
        :param repository: The repository to query in.
444
        :return: A smart server response of ('yes', ) if the repository is
445
            shared, and ('no', ) if it is not.
446
        """
447
        if repository.is_shared():
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
448
            return SuccessfulSmartServerResponse(('yes', ))
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
449
        else:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
450
            return SuccessfulSmartServerResponse(('no', ))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
451
452
6263.2.1 by Jelmer Vernooij
Add hpss call ``Repository.make_working_trees``
453
class SmartServerRepositoryMakeWorkingTrees(SmartServerRepositoryRequest):
454
455
    def do_repository_request(self, repository):
456
        """Return the result of repository.make_working_trees().
457
6263.2.2 by Jelmer Vernooij
Document when Repository.make_working_trees was introduced.
458
        Introduced in bzr 2.5.0.
459
6263.2.1 by Jelmer Vernooij
Add hpss call ``Repository.make_working_trees``
460
        :param repository: The repository to query in.
461
        :return: A smart server response of ('yes', ) if the repository uses
462
            working trees, and ('no', ) if it is not.
463
        """
464
        if repository.make_working_trees():
465
            return SuccessfulSmartServerResponse(('yes', ))
466
        else:
467
            return SuccessfulSmartServerResponse(('no', ))
468
469
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
470
class SmartServerRepositoryLockWrite(SmartServerRepositoryRequest):
471
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
472
    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
473
        # XXX: this probably should not have a token.
474
        if token == '':
475
            token = None
476
        try:
5200.3.3 by Robert Collins
Lock methods on ``Tree``, ``Branch`` and ``Repository`` are now
477
            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
478
        except errors.LockContention, e:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
479
            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.
480
        except errors.UnlockableTransport:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
481
            return FailedSmartServerResponse(('UnlockableTransport',))
2872.5.3 by Martin Pool
Pass back LockFailed from smart server lock methods
482
        except errors.LockFailed, e:
483
            return FailedSmartServerResponse(('LockFailed',
484
                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.
485
        if token is not None:
486
            repository.leave_lock_in_place()
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
487
        repository.unlock()
488
        if token is None:
489
            token = ''
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
490
        return SuccessfulSmartServerResponse(('ok', token))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
491
492
4060.1.5 by Robert Collins
Verb change name requested by Andrew.
493
class SmartServerRepositoryGetStream(SmartServerRepositoryRequest):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
494
495
    def do_repository_request(self, repository, to_network_name):
496
        """Get a stream for inserting into a to_format repository.
497
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.
498
        The request body is 'search_bytes', a description of the revisions
499
        being requested.
500
501
        In 2.3 this verb added support for search_bytes == 'everything'.  Older
502
        implementations will respond with a BadSearch error, and clients should
503
        catch this and fallback appropriately.
504
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
505
        :param repository: The repository to stream from.
506
        :param to_network_name: The network name of the format of the target
507
            repository.
508
        """
509
        self._to_format = network_format_registry.get(to_network_name)
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
510
        if self._should_fake_unknown():
511
            return FailedSmartServerResponse(
512
                ('UnknownMethod', 'Repository.get_stream'))
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
513
        return None # Signal that we want a body.
514
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
515
    def _should_fake_unknown(self):
4476.3.80 by Andrew Bennetts
Comment/docstring tweaks prompted by review.
516
        """Return True if we should return UnknownMethod to the client.
517
        
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.
518
        This is a workaround for bugs in pre-1.19 clients that claim to
519
        support receiving streams of CHK repositories.  The pre-1.19 client
4476.3.80 by Andrew Bennetts
Comment/docstring tweaks prompted by review.
520
        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.
521
        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.
522
        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.
523
        rich-roots.  After 1.19 the client can also accept inventory-deltas
4476.3.80 by Andrew Bennetts
Comment/docstring tweaks prompted by review.
524
        (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.
525
        Repository.get_stream_1.19 verb instead of this one.
4476.3.80 by Andrew Bennetts
Comment/docstring tweaks prompted by review.
526
        So: if this repository is CHK, and the to_format doesn't match,
527
        we should just fake an UnknownSmartMethod error so that the client
528
        will fallback to VFS, rather than sending it a stream we know it
529
        cannot handle.
530
        """
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
531
        from_format = self._repository._format
532
        to_format = self._to_format
533
        if not from_format.supports_chks:
534
            # Source not CHK: that's ok
535
            return False
536
        if (to_format.supports_chks and
537
            from_format.repository_class is to_format.repository_class and
538
            from_format._serializer == to_format._serializer):
539
            # Source is CHK, but target matches: that's ok
540
            # (e.g. 2a->2a, or CHK2->2a)
541
            return False
542
        # Source is CHK, and target is not CHK or incompatible CHK.  We can't
543
        # generate a compatible stream.
544
        return True
545
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
546
    def do_body(self, body_bytes):
547
        repository = self._repository
548
        repository.lock_read()
549
        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.
550
            search_result, error = self.recreate_search(repository, body_bytes,
551
                discard_excess=True)
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
552
            if error is not None:
553
                repository.unlock()
554
                return error
555
            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.
556
            stream = source.get_stream(search_result)
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
557
        except Exception:
558
            exc_info = sys.exc_info()
559
            try:
560
                # On non-error, unlocking is done by the body stream handler.
561
                repository.unlock()
562
            finally:
563
                raise exc_info[0], exc_info[1], exc_info[2]
564
        return SuccessfulSmartServerResponse(('ok',),
565
            body_stream=self.body_stream(stream, repository))
566
567
    def body_stream(self, stream, repository):
568
        byte_stream = _stream_to_byte_stream(stream, repository._format)
569
        try:
570
            for bytes in byte_stream:
571
                yield bytes
572
        except errors.RevisionNotPresent, e:
573
            # This shouldn't be able to happen, but as we don't buffer
574
            # everything it can in theory happen.
575
            repository.unlock()
576
            yield FailedSmartServerResponse(('NoSuchRevision', e.revision_id))
577
        else:
578
            repository.unlock()
579
580
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.
581
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.
582
    """The same as Repository.get_stream, but will return stream CHK formats to
583
    clients.
584
585
    See SmartServerRepositoryGetStream._should_fake_unknown.
586
    
587
    New in 1.19.
588
    """
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
589
590
    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.
591
        """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.
592
        return False
593
594
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
595
def _stream_to_byte_stream(stream, src_format):
596
    """Convert a record stream to a self delimited byte stream."""
597
    pack_writer = pack.ContainerSerialiser()
598
    yield pack_writer.begin()
599
    yield pack_writer.bytes_record(src_format.network_name(), '')
600
    for substream_type, substream in stream:
601
        for record in substream:
602
            if record.storage_kind in ('chunked', 'fulltext'):
603
                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.
604
            elif record.storage_kind == 'absent':
605
                raise ValueError("Absent factory for %s" % (record.key,))
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
606
            else:
607
                serialised = record.get_bytes_as(record.storage_kind)
608
            if serialised:
609
                # Some streams embed the whole stream into the wire
610
                # representation of the first record, which means that
611
                # later records have no wire representation: we skip them.
612
                yield pack_writer.bytes_record(serialised, [(substream_type,)])
613
    yield pack_writer.end()
614
615
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
616
class _ByteStreamDecoder(object):
617
    """Helper for _byte_stream_to_stream.
618
4634.19.2 by Robert Collins
Review feedback.
619
    The expected usage of this class is via the function _byte_stream_to_stream
620
    which creates a _ByteStreamDecoder, pops off the stream format and then
621
    yields the output of record_stream(), the main entry point to
622
    _ByteStreamDecoder.
623
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
624
    Broadly this class has to unwrap two layers of iterators:
625
    (type, substream)
626
    (substream details)
627
628
    This is complicated by wishing to return type, iterator_for_type, but
629
    getting the data for iterator_for_type when we find out type: we can't
630
    simply pass a generator down to the NetworkRecordStream parser, instead
631
    we have a little local state to seed each NetworkRecordStream instance,
632
    and gather the type that we'll be yielding.
633
634
    :ivar byte_stream: The byte stream being decoded.
635
    :ivar stream_decoder: A pack parser used to decode the bytestream
636
    :ivar current_type: The current type, used to join adjacent records of the
637
        same type into a single stream.
638
    :ivar first_bytes: The first bytes to give the next NetworkRecordStream.
639
    """
640
5195.3.23 by Parth Malwankar
moved progress bar logic to SourceStream.
641
    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.
642
        """Create a _ByteStreamDecoder."""
643
        self.stream_decoder = pack.ContainerPushParser()
644
        self.current_type = None
645
        self.first_bytes = None
646
        self.byte_stream = byte_stream
5195.3.27 by Parth Malwankar
code cleanup and comments.
647
        self._record_counter = record_counter
5195.3.23 by Parth Malwankar
moved progress bar logic to SourceStream.
648
        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.
649
650
    def iter_stream_decoder(self):
651
        """Iterate the contents of the pack from stream_decoder."""
652
        # dequeue pending items
653
        for record in self.stream_decoder.read_pending_records():
654
            yield record
655
        # Pull bytes of the wire, decode them to records, yield those records.
656
        for bytes in self.byte_stream:
657
            self.stream_decoder.accept_bytes(bytes)
658
            for record in self.stream_decoder.read_pending_records():
659
                yield record
660
661
    def iter_substream_bytes(self):
662
        if self.first_bytes is not None:
663
            yield self.first_bytes
664
            # If we run out of pack records, single the outer layer to stop.
665
            self.first_bytes = None
666
        for record in self.iter_pack_records:
667
            record_names, record_bytes = record
668
            record_name, = record_names
669
            substream_type = record_name[0]
670
            if substream_type != self.current_type:
671
                # end of a substream, seed the next substream.
672
                self.current_type = substream_type
673
                self.first_bytes = record_bytes
674
                return
675
            yield record_bytes
676
677
    def record_stream(self):
678
        """Yield substream_type, substream from the byte stream."""
5195.3.27 by Parth Malwankar
code cleanup and comments.
679
        def wrap_and_count(pb, rc, substream):
680
            """Yield records from stream while showing progress."""
681
            counter = 0
682
            if rc:
683
                if self.current_type != 'revisions' and self.key_count != 0:
684
                    # As we know the number of revisions now (in self.key_count)
685
                    # we can setup and use record_counter (rc).
686
                    if not rc.is_initialized():
687
                        rc.setup(self.key_count, self.key_count)
688
            for record in substream.read():
689
                if rc:
690
                    if rc.is_initialized() and counter == rc.STEP:
691
                        rc.increment(counter)
692
                        pb.update('Estimate', rc.current, rc.max)
693
                        counter = 0
694
                    if self.current_type == 'revisions':
695
                        # Total records is proportional to number of revs
696
                        # to fetch. With remote, we used self.key_count to
697
                        # track the number of revs. Once we have the revs
698
                        # counts in self.key_count, the progress bar changes
699
                        # from 'Estimating..' to 'Estimate' above.
700
                        self.key_count += 1
701
                        if counter == rc.STEP:
702
                            pb.update('Estimating..', self.key_count)
703
                            counter = 0
704
                counter += 1
705
                yield record
706
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
707
        self.seed_state()
5195.3.23 by Parth Malwankar
moved progress bar logic to SourceStream.
708
        pb = ui.ui_factory.nested_progress_bar()
5195.3.27 by Parth Malwankar
code cleanup and comments.
709
        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.
710
        # Make and consume sub generators, one per substream type:
711
        while self.first_bytes is not None:
712
            substream = NetworkRecordStream(self.iter_substream_bytes())
713
            # after substream is fully consumed, self.current_type is set to
714
            # the next type, and self.first_bytes is set to the matching bytes.
5195.3.27 by Parth Malwankar
code cleanup and comments.
715
            yield self.current_type, wrap_and_count(pb, rc, substream)
716
        if rc:
717
            pb.update('Done', rc.max, rc.max)
5195.3.23 by Parth Malwankar
moved progress bar logic to SourceStream.
718
        pb.finished()
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
719
720
    def seed_state(self):
721
        """Prepare the _ByteStreamDecoder to decode from the pack stream."""
722
        # Set a single generator we can use to get data from the pack stream.
723
        self.iter_pack_records = self.iter_stream_decoder()
724
        # Seed the very first subiterator with content; after this each one
725
        # seeds the next.
726
        list(self.iter_substream_bytes())
727
728
5195.3.23 by Parth Malwankar
moved progress bar logic to SourceStream.
729
def _byte_stream_to_stream(byte_stream, record_counter=None):
4060.1.5 by Robert Collins
Verb change name requested by Andrew.
730
    """Convert a byte stream into a format and a stream.
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
731
732
    :param byte_stream: A bytes iterator, as output by _stream_to_byte_stream.
733
    :return: (RepositoryFormat, stream_generator)
734
    """
5195.3.23 by Parth Malwankar
moved progress bar logic to SourceStream.
735
    decoder = _ByteStreamDecoder(byte_stream, record_counter)
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
736
    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.
737
        decoder.stream_decoder.accept_bytes(bytes)
738
        for record in decoder.stream_decoder.read_pending_records(max=1):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
739
            record_names, src_format_name = record
740
            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.
741
            return src_format, decoder.record_stream()
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
742
743
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
744
class SmartServerRepositoryUnlock(SmartServerRepositoryRequest):
745
746
    def do_repository_request(self, repository, token):
747
        try:
748
            repository.lock_write(token=token)
749
        except errors.TokenMismatch, e:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
750
            return FailedSmartServerResponse(('TokenMismatch',))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
751
        repository.dont_leave_lock_in_place()
752
        repository.unlock()
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
753
        return SuccessfulSmartServerResponse(('ok',))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
754
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
755
6280.6.2 by Jelmer Vernooij
Add HPSS calls Repository.get_physical_lock_status and Branch.get_physical_lock_status.
756
class SmartServerRepositoryGetPhysicalLockStatus(SmartServerRepositoryRequest):
757
    """Get the physical lock status for a repository.
758
759
    New in 2.5.
760
    """
761
762
    def do_repository_request(self, repository):
763
        if repository.get_physical_lock_status():
764
            return SuccessfulSmartServerResponse(('yes', ))
765
        else:
766
            return SuccessfulSmartServerResponse(('no', ))
767
768
4017.3.4 by Robert Collins
Create a verb for Repository.set_make_working_trees.
769
class SmartServerRepositorySetMakeWorkingTrees(SmartServerRepositoryRequest):
770
771
    def do_repository_request(self, repository, str_bool_new_value):
772
        if str_bool_new_value == 'True':
773
            new_value = True
774
        else:
775
            new_value = False
776
        repository.set_make_working_trees(new_value)
777
        return SuccessfulSmartServerResponse(('ok',))
778
779
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
780
class SmartServerRepositoryTarball(SmartServerRepositoryRequest):
2018.18.11 by Martin Pool
merge hpss changes
781
    """Get the raw repository files as a tarball.
782
783
    The returned tarball contains a .bzr control directory which in turn
784
    contains a repository.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
785
786
    This takes one parameter, compression, which currently must be
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
787
    "", "gz", or "bz2".
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
788
789
    This is used to implement the Repository.copy_content_into operation.
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
790
    """
791
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
792
    def do_repository_request(self, repository, compression):
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
793
        tmp_dirname, tmp_repo = self._copy_to_tempdir(repository)
2018.18.5 by Martin Pool
Repository.tarball locks repository while running for consistency
794
        try:
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
795
            controldir_name = tmp_dirname + '/.bzr'
796
            return self._tarfile_response(controldir_name, compression)
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
797
        finally:
798
            osutils.rmtree(tmp_dirname)
799
800
    def _copy_to_tempdir(self, from_repo):
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
801
        tmp_dirname = osutils.mkdtemp(prefix='tmpbzrclone')
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
802
        tmp_bzrdir = from_repo.bzrdir._format.initialize(tmp_dirname)
803
        tmp_repo = from_repo._format.initialize(tmp_bzrdir)
804
        from_repo.copy_content_into(tmp_repo)
805
        return tmp_dirname, tmp_repo
806
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
807
    def _tarfile_response(self, tmp_dirname, compression):
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
808
        temp = tempfile.NamedTemporaryFile()
809
        try:
2557.1.1 by Martin Pool
[BUG 119330] Fix tempfile permissions error in smart server tar bundling (under windows) (Martin_)
810
            self._tarball_of_dir(tmp_dirname, compression, temp.file)
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
811
            # all finished; write the tempfile out to the network
812
            temp.seek(0)
2420.2.2 by Andrew Bennetts
Merge tarball branch that's already with PQM, resolving conflicts.
813
            return SuccessfulSmartServerResponse(('ok',), temp.read())
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
814
            # FIXME: Don't read the whole thing into memory here; rather stream
815
            # it out from the file onto the network. mbp 20070411
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
816
        finally:
817
            temp.close()
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
818
2557.1.1 by Martin Pool
[BUG 119330] Fix tempfile permissions error in smart server tar bundling (under windows) (Martin_)
819
    def _tarball_of_dir(self, dirname, compression, ofile):
5017.2.4 by Martin Pool
Move or remove some unconditionally loaded code
820
        import tarfile
2571.2.2 by Ian Clatworthy
use basename as poolie recommended
821
        filename = os.path.basename(ofile.name)
822
        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
823
            mode='w|' + compression)
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
824
        try:
825
            # The tarball module only accepts ascii names, and (i guess)
826
            # packs them with their 8bit names.  We know all the files
827
            # within the repository have ASCII names so the should be safe
828
            # to pack in.
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
829
            dirname = dirname.encode(sys.getfilesystemencoding())
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
830
            # python's tarball module includes the whole path by default so
831
            # override it
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
832
            if not dirname.endswith('.bzr'):
833
                raise ValueError(dirname)
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
834
            tarball.add(dirname, '.bzr') # recursive by default
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
835
        finally:
836
            tarball.close()
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
837
838
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
839
class SmartServerRepositoryInsertStreamLocked(SmartServerRepositoryRequest):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
840
    """Insert a record stream from a RemoteSink into a repository.
841
842
    This gets bytes pushed to it by the network infrastructure and turns that
843
    into a bytes iterator using a thread. That is then processed by
844
    _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.
845
846
    New in 1.14.
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
847
    """
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
848
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
849
    def do_repository_request(self, repository, resume_tokens, lock_token):
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
850
        """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.
851
        repository.lock_write(token=lock_token)
852
        self.do_insert_stream_request(repository, resume_tokens)
853
854
    def do_insert_stream_request(self, repository, resume_tokens):
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
855
        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.
856
        self.tokens = tokens
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
857
        self.repository = repository
858
        self.queue = Queue.Queue()
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
859
        self.insert_thread = threading.Thread(target=self._inserter_thread)
860
        self.insert_thread.start()
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
861
862
    def do_chunk(self, body_stream_chunk):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
863
        self.queue.put(body_stream_chunk)
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
864
865
    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.
866
        try:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
867
            src_format, stream = _byte_stream_to_stream(
868
                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.
869
            self.insert_result = self.repository._get_sink().insert_stream(
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
870
                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.
871
            self.insert_ok = True
872
        except:
873
            self.insert_exception = sys.exc_info()
874
            self.insert_ok = False
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
875
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
876
    def blocking_byte_stream(self):
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
877
        while True:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
878
            bytes = self.queue.get()
879
            if bytes is StopIteration:
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
880
                return
881
            else:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
882
                yield bytes
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
883
884
    def do_end(self):
885
        self.queue.put(StopIteration)
886
        if self.insert_thread is not None:
887
            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.
888
        if not self.insert_ok:
889
            exc_info = self.insert_exception
890
            raise exc_info[0], exc_info[1], exc_info[2]
891
        write_group_tokens, missing_keys = self.insert_result
892
        if write_group_tokens or missing_keys:
893
            # bzip needed? missing keys should typically be a small set.
894
            # Should this be a streaming body response ?
895
            missing_keys = sorted(missing_keys)
896
            bytes = bencode.bencode((write_group_tokens, missing_keys))
897
            self.repository.unlock()
898
            return SuccessfulSmartServerResponse(('missing-basis', bytes))
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
899
        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.
900
            self.repository.unlock()
901
            return SuccessfulSmartServerResponse(('ok', ))
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
902
903
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.
904
class SmartServerRepositoryInsertStream_1_19(SmartServerRepositoryInsertStreamLocked):
4476.3.15 by Andrew Bennetts
Partially working fallback for pre-1.17 servers.
905
    """Insert a record stream from a RemoteSink into a repository.
906
907
    Same as SmartServerRepositoryInsertStreamLocked, except:
908
     - the lock token argument is optional
909
     - servers that implement this verb accept 'inventory-delta' records in the
910
       stream.
911
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.
912
    New in 1.19.
4476.3.15 by Andrew Bennetts
Partially working fallback for pre-1.17 servers.
913
    """
914
915
    def do_repository_request(self, repository, resume_tokens, lock_token=None):
916
        """StreamSink.insert_stream for a remote repository."""
917
        SmartServerRepositoryInsertStreamLocked.do_repository_request(
918
            self, repository, resume_tokens, lock_token)
919
920
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
921
class SmartServerRepositoryInsertStream(SmartServerRepositoryInsertStreamLocked):
922
    """Insert a record stream from a RemoteSink into an unlocked repository.
923
924
    This is the same as SmartServerRepositoryInsertStreamLocked, except it
925
    takes no lock_tokens; i.e. it works with an unlocked (or lock-free, e.g.
926
    like pack format) repository.
927
928
    New in 1.13.
929
    """
930
931
    def do_repository_request(self, repository, resume_tokens):
932
        """StreamSink.insert_stream for a remote repository."""
933
        repository.lock_write()
934
        self.do_insert_stream_request(repository, resume_tokens)
935
936
6268.1.2 by Jelmer Vernooij
Initial work on Repository.add_signature_text.
937
class SmartServerRepositoryAddSignatureText(SmartServerRepositoryRequest):
938
    """Add a revision signature text.
939
940
    New in 2.5.
941
    """
942
6268.1.6 by Jelmer Vernooij
Fix add_signature_text.
943
    def do_repository_request(self, repository, lock_token, write_group_tokens,
944
            revision_id):
6268.1.7 by Jelmer Vernooij
Docstrings.
945
        """Add a revision signature text.
946
947
        :param repository: Repository to operate on
948
        :param lock_token: Lock token
949
        :param write_group_tokens: Write group tokens
950
        :param revision_id: Revision for which to add signature
951
        """
6268.1.6 by Jelmer Vernooij
Fix add_signature_text.
952
        self._lock_token = lock_token
953
        self._write_group_tokens = write_group_tokens
6268.1.2 by Jelmer Vernooij
Initial work on Repository.add_signature_text.
954
        self._revision_id = revision_id
955
        return None
956
957
    def do_body(self, body_bytes):
6268.1.7 by Jelmer Vernooij
Docstrings.
958
        """Add a signature text.
959
960
        :param body_bytes: GPG signature text
961
        :return: SuccessfulSmartServerResponse with arguments 'ok' and
962
            the list of new write group tokens.
963
        """
6268.1.6 by Jelmer Vernooij
Fix add_signature_text.
964
        self._repository.lock_write(token=self._lock_token)
965
        try:
966
            self._repository.resume_write_group(self._write_group_tokens)
967
            try:
968
                self._repository.add_signature_text(self._revision_id, body_bytes)
969
            finally:
970
                new_write_group_tokens = self._repository.suspend_write_group()
971
        finally:
972
            self._repository.unlock()
973
        return SuccessfulSmartServerResponse(('ok', ) + tuple(new_write_group_tokens))
6268.1.4 by Jelmer Vernooij
Merge write group improvements.
974
975
6280.7.2 by Jelmer Vernooij
Add HPSS calls ``Repository.start_write_group``, ``Repository.abort_write_group`` and ``Repository.commit_write_group``.
976
class SmartServerRepositoryStartWriteGroup(SmartServerRepositoryRequest):
977
    """Start a write group.
978
979
    New in 2.5.
980
    """
981
982
    def do_repository_request(self, repository, lock_token):
983
        """Start a write group."""
984
        repository.lock_write(token=lock_token)
985
        try:
986
            repository.start_write_group()
6280.7.9 by Jelmer Vernooij
test repositories with unsuspendable write groups.
987
            try:
988
                tokens = repository.suspend_write_group()
989
            except errors.UnsuspendableWriteGroup:
990
                return FailedSmartServerResponse(('UnsuspendableWriteGroup',))
6280.7.2 by Jelmer Vernooij
Add HPSS calls ``Repository.start_write_group``, ``Repository.abort_write_group`` and ``Repository.commit_write_group``.
991
        finally:
992
            repository.unlock()
6280.7.6 by Jelmer Vernooij
Fix remaining tests.
993
        return SuccessfulSmartServerResponse(('ok', tokens))
6280.7.2 by Jelmer Vernooij
Add HPSS calls ``Repository.start_write_group``, ``Repository.abort_write_group`` and ``Repository.commit_write_group``.
994
995
996
class SmartServerRepositoryCommitWriteGroup(SmartServerRepositoryRequest):
997
    """Commit a write group.
998
999
    New in 2.5.
1000
    """
1001
1002
    def do_repository_request(self, repository, lock_token,
6280.7.4 by Jelmer Vernooij
pass write group tokens as list/tuple.
1003
            write_group_tokens):
6280.7.2 by Jelmer Vernooij
Add HPSS calls ``Repository.start_write_group``, ``Repository.abort_write_group`` and ``Repository.commit_write_group``.
1004
        """Commit a write group."""
1005
        repository.lock_write(token=lock_token)
1006
        try:
6280.7.5 by Jelmer Vernooij
Bunch of test fixes.
1007
            try:
1008
                repository.resume_write_group(write_group_tokens)
1009
            except errors.UnresumableWriteGroup, e:
1010
                return FailedSmartServerResponse(
1011
                    ('UnresumableWriteGroup', e.write_groups, e.reason))
1012
            try:
1013
                repository.commit_write_group()
1014
            except:
1015
                write_group_tokens = repository.suspend_write_group()
1016
                # FIXME JRV 2011-11-19: What if the write_group_tokens
1017
                # have changed?
1018
                raise
6280.7.2 by Jelmer Vernooij
Add HPSS calls ``Repository.start_write_group``, ``Repository.abort_write_group`` and ``Repository.commit_write_group``.
1019
        finally:
1020
            repository.unlock()
1021
        return SuccessfulSmartServerResponse(('ok', ))
1022
1023
1024
class SmartServerRepositoryAbortWriteGroup(SmartServerRepositoryRequest):
1025
    """Abort a write group.
1026
1027
    New in 2.5.
1028
    """
1029
6280.7.5 by Jelmer Vernooij
Bunch of test fixes.
1030
    def do_repository_request(self, repository, lock_token, write_group_tokens):
6280.7.2 by Jelmer Vernooij
Add HPSS calls ``Repository.start_write_group``, ``Repository.abort_write_group`` and ``Repository.commit_write_group``.
1031
        """Abort a write group."""
1032
        repository.lock_write(token=lock_token)
1033
        try:
6280.7.5 by Jelmer Vernooij
Bunch of test fixes.
1034
            try:
1035
                repository.resume_write_group(write_group_tokens)
1036
            except errors.UnresumableWriteGroup, e:
1037
                return FailedSmartServerResponse(
1038
                    ('UnresumableWriteGroup', e.write_groups, e.reason))
1039
                repository.abort_write_group()
6280.7.2 by Jelmer Vernooij
Add HPSS calls ``Repository.start_write_group``, ``Repository.abort_write_group`` and ``Repository.commit_write_group``.
1040
        finally:
1041
            repository.unlock()
1042
        return SuccessfulSmartServerResponse(('ok', ))
6280.7.6 by Jelmer Vernooij
Fix remaining tests.
1043
1044
1045
class SmartServerRepositoryCheckWriteGroup(SmartServerRepositoryRequest):
1046
    """Check that a write group is still valid.
1047
1048
    New in 2.5.
1049
    """
1050
1051
    def do_repository_request(self, repository, lock_token, write_group_tokens):
1052
        """Abort a write group."""
1053
        repository.lock_write(token=lock_token)
1054
        try:
1055
            try:
1056
                repository.resume_write_group(write_group_tokens)
1057
            except errors.UnresumableWriteGroup, e:
1058
                return FailedSmartServerResponse(
1059
                    ('UnresumableWriteGroup', e.write_groups, e.reason))
1060
            else:
1061
                repository.suspend_write_group()
1062
        finally:
1063
            repository.unlock()
1064
        return SuccessfulSmartServerResponse(('ok', ))
6280.7.13 by Jelmer Vernooij
Merge bzr.dev.
1065
6268.1.13 by Jelmer Vernooij
Merge bzr.dev.
1066
6280.3.2 by Jelmer Vernooij
Add smart side of RemoteRepository.all_revision_ids().
1067
class SmartServerRepositoryAllRevisionIds(SmartServerRepositoryRequest):
1068
    """Retrieve all of the revision ids in a repository.
1069
1070
    New in 2.5.
1071
    """
1072
1073
    def do_repository_request(self, repository):
1074
        revids = repository.all_revision_ids()
1075
        return SuccessfulSmartServerResponse(("ok", ), "\n".join(revids))
6305.2.2 by Jelmer Vernooij
Add smart side of pack.
1076
1077
1078
class SmartServerRepositoryPack(SmartServerRepositoryRequest):
1079
    """Pack a repository.
1080
1081
    New in 2.5.
1082
    """
1083
6305.2.4 by Jelmer Vernooij
Fix tests.
1084
    def do_repository_request(self, repository, lock_token, clean_obsolete_packs):
6305.2.3 by Jelmer Vernooij
Store hint in body.
1085
        self._repository = repository
6305.2.4 by Jelmer Vernooij
Fix tests.
1086
        self._lock_token = lock_token
1087
        if clean_obsolete_packs == 'True':
1088
            self._clean_obsolete_packs = True
1089
        else:
1090
            self._clean_obsolete_packs = False
6305.2.3 by Jelmer Vernooij
Store hint in body.
1091
        return None
1092
1093
    def do_body(self, body_bytes):
1094
        if body_bytes == "":
1095
            hint = None
1096
        else:
1097
            hint = body_bytes.splitlines()
6305.2.4 by Jelmer Vernooij
Fix tests.
1098
        self._repository.lock_write(token=self._lock_token)
1099
        try:
1100
            self._repository.pack(hint, self._clean_obsolete_packs)
1101
        finally:
1102
            self._repository.unlock()
6305.2.2 by Jelmer Vernooij
Add smart side of pack.
1103
        return SuccessfulSmartServerResponse(("ok", ), )