/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
1
# Copyright (C) 2006, 2007 Canonical Ltd
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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
2535.3.14 by Andrew Bennetts
Move serialising repo data stream to bytes into smart protocol.
20
from cStringIO import StringIO
2571.2.2 by Ian Clatworthy
use basename as poolie recommended
21
import os
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
22
import sys
23
import tempfile
24
import tarfile
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
25
26
from bzrlib import errors
27
from bzrlib.bzrdir import BzrDir
2535.4.30 by Andrew Bennetts
Use ContainerSerialiser rather than ContainerWriter in bzrlib/smart/repository.py.
28
from bzrlib.pack import ContainerSerialiser
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
29
from bzrlib.smart.request import (
30
    FailedSmartServerResponse,
31
    SmartServerRequest,
32
    SuccessfulSmartServerResponse,
33
    )
3287.6.8 by Robert Collins
Reduce code duplication as per review.
34
from bzrlib.repository import _strip_NULL_ghosts
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
35
from bzrlib import revision as _mod_revision
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
36
37
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
38
class SmartServerRepositoryRequest(SmartServerRequest):
39
    """Common base class for Repository requests."""
40
41
    def do(self, path, *args):
42
        """Execute a repository request.
43
        
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
44
        The repository must be at the exact path - no searching is done.
45
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
46
        The actual logic is delegated to self.do_repository_request.
47
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
48
        :param path: The path for the repository.
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
49
        :return: A smart server from self.do_repository_request().
50
        """
51
        transport = self._backing_transport.clone(path)
52
        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.
53
        # Save the repository for use with do_body.
54
        self._repository = bzrdir.open_repository()
55
        return self.do_repository_request(self._repository, *args)
56
57
    def do_repository_request(self, repository, *args):
58
        """Override to provide an implementation for a verb."""
59
        # No-op for verbs that take bodies (None as a result indicates a body
60
        # is expected)
61
        return None
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
62
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.
63
    def recreate_search(self, repository, recipe_bytes):
64
        lines = recipe_bytes.split('\n')
65
        start_keys = set(lines[0].split(' '))
66
        exclude_keys = set(lines[1].split(' '))
67
        revision_count = int(lines[2])
68
        repository.lock_read()
69
        try:
70
            search = repository.get_graph()._make_breadth_first_searcher(
71
                start_keys)
72
            while True:
73
                try:
74
                    next_revs = search.next()
75
                except StopIteration:
76
                    break
77
                search.stop_searching_any(exclude_keys.intersection(next_revs))
78
            search_result = search.get_result()
79
            if search_result.get_recipe()[2] != revision_count:
80
                # we got back a different amount of data than expected, this
81
                # gets reported as NoSuchRevision, because less revisions
82
                # indicates missing revisions, and more should never happen as
83
                # the excludes list considers ghosts and ensures that ghost
84
                # filling races are not a problem.
85
                return (None, FailedSmartServerResponse(('NoSuchRevision',)))
86
            return (search, None)
87
        finally:
88
            repository.unlock()
89
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
90
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
91
class SmartServerRepositoryReadLocked(SmartServerRepositoryRequest):
92
    """Calls self.do_readlocked_repository_request."""
93
94
    def do_repository_request(self, repository, *args):
95
        """Read lock a repository for do_readlocked_repository_request."""
96
        repository.lock_read()
97
        try:
98
            return self.do_readlocked_repository_request(repository, *args)
99
        finally:
100
            repository.unlock()
101
102
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
103
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.
104
    """Bzr 1.2+ - get parent data for revisions during a graph search."""
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
105
    
106
    def do_repository_request(self, repository, *revision_ids):
107
        """Get parent details for some revisions.
108
        
109
        All the parents for revision_ids are returned. Additionally up to 64KB
110
        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.
111
        from revision_ids is returned. The verb takes a body containing the
112
        current search state, see do_body for details.
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
113
114
        :param repository: The repository to query in.
3172.5.8 by Robert Collins
Review feedback.
115
        :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.
116
        """
117
        self._revision_ids = revision_ids
118
        return None # Signal that we want a body.
119
120
    def do_body(self, body_bytes):
121
        """Process the current search state and perform the parent lookup.
122
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
123
        :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.
124
            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.
125
            format as Repository.get_revision_graph) which has been bz2
126
            compressed.
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
127
        """
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.
128
        repository = self._repository
129
        repository.lock_read()
130
        try:
131
            return self._do_repository_request(body_bytes)
132
        finally:
133
            repository.unlock()
134
135
    def _do_repository_request(self, body_bytes):
136
        repository = self._repository
137
        revision_ids = set(self._revision_ids)
138
        search, error = self.recreate_search(repository, body_bytes)
139
        if error is not None:
140
            return error
141
        # TODO might be nice to start up the search again; but thats not
142
        # written or tested yet.
143
        client_seen_revs = set(search.get_result().get_keys())
144
        # Always include the requested ids.
145
        client_seen_revs.difference_update(revision_ids)
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
146
        lines = []
147
        repo_graph = repository.get_graph()
148
        result = {}
149
        queried_revs = set()
150
        size_so_far = 0
151
        next_revs = revision_ids
152
        first_loop_done = False
153
        while next_revs:
154
            queried_revs.update(next_revs)
155
            parent_map = repo_graph.get_parent_map(next_revs)
156
            next_revs = set()
157
            for revision_id, parents in parent_map.iteritems():
158
                # adjust for the wire
159
                if parents == (_mod_revision.NULL_REVISION,):
160
                    parents = ()
161
                # prepare the next query
162
                next_revs.update(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.
163
                if revision_id not in client_seen_revs:
164
                    # Client does not have this revision, give it to it.
165
                    # add parents to the result
166
                    result[revision_id] = parents
167
                    # Approximate the serialized cost of this revision_id.
168
                    size_so_far += 2 + len(revision_id) + sum(map(len, parents))
169
            # get all the directly asked for parents, and then flesh out to
170
            # 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.
171
            # 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.
172
            # estimated compression ratio taken from bzr.dev itself.
3211.5.3 by Robert Collins
Adjust size of batch and change gzip comments to bzip2.
173
            if 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.
174
                next_revs = set()
175
                break
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
176
            # don't query things we've already queried
177
            next_revs.difference_update(queried_revs)
178
            first_loop_done = True
179
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.
180
        # sorting trivially puts lexographically similar revision ids together.
181
        # Compression FTW.
182
        for revision, parents in sorted(result.items()):
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
183
            lines.append(' '.join((revision, ) + tuple(parents)))
184
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.
185
        return SuccessfulSmartServerResponse(
3211.5.2 by Robert Collins
Change RemoteRepository.get_parent_map to use bz2 not gzip for compression.
186
            ('ok', ), bz2.compress('\n'.join(lines)))
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
187
188
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
189
class SmartServerRepositoryGetRevisionGraph(SmartServerRepositoryReadLocked):
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
190
    
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
191
    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)
192
        """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.
193
194
        Deprecated as of bzr 1.4, but supported for older clients.
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
195
        
196
        :param repository: The repository to query in.
197
        :param revision_id: The utf8 encoded revision_id to get a graph from.
198
        :return: A smart server response where the body contains an utf8
199
            encoded flattened list of the revision graph.
200
        """
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
201
        if not revision_id:
202
            revision_id = None
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
203
204
        lines = []
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
205
        graph = repository.get_graph()
206
        if revision_id:
207
            search_ids = [revision_id]
208
        else:
209
            search_ids = repository.all_revision_ids()
210
        search = graph._make_breadth_first_searcher(search_ids)
211
        transitive_ids = set()
212
        map(transitive_ids.update, list(search))
213
        parent_map = graph.get_parent_map(transitive_ids)
3287.6.8 by Robert Collins
Reduce code duplication as per review.
214
        revision_graph = _strip_NULL_ghosts(parent_map)
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
215
        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.
216
            # Note that we return an empty body, rather than omitting the body.
217
            # This way the client knows that it can always expect to find a body
218
            # 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.
219
            return FailedSmartServerResponse(('nosuchrevision', revision_id), '')
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
220
221
        for revision, parents in revision_graph.items():
2592.3.50 by Robert Collins
Merge bzr.dev.
222
            lines.append(' '.join((revision, ) + tuple(parents)))
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
223
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
224
        return SuccessfulSmartServerResponse(('ok', ), '\n'.join(lines))
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
225
226
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
227
class SmartServerRequestHasRevision(SmartServerRepositoryRequest):
228
229
    def do_repository_request(self, repository, revision_id):
230
        """Return ok if a specific revision is in the repository at path.
231
232
        :param repository: The repository to query in.
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
233
        :param revision_id: The utf8 encoded revision_id to lookup.
234
        :return: A smart server response of ('ok', ) if the revision is
235
            present.
236
        """
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
237
        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.
238
            return SuccessfulSmartServerResponse(('yes', ))
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
239
        else:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
240
            return SuccessfulSmartServerResponse(('no', ))
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
241
242
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
243
class SmartServerRepositoryGatherStats(SmartServerRepositoryRequest):
244
245
    def do_repository_request(self, repository, revid, committers):
246
        """Return the result of repository.gather_stats().
247
248
        :param repository: The repository to query in.
249
        :param revid: utf8 encoded rev id or an empty string to indicate None
250
        :param committers: 'yes' or 'no'.
251
252
        :return: A SmartServerResponse ('ok',), a encoded body looking like
253
              committers: 1
254
              firstrev: 1234.230 0
255
              latestrev: 345.700 3600
256
              revisions: 2
257
              size:45
258
259
              But containing only fields returned by the gather_stats() call
260
        """
261
        if revid == '':
262
            decoded_revision_id = None
263
        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.
264
            decoded_revision_id = revid
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
265
        if committers == 'yes':
266
            decoded_committers = True
267
        else:
268
            decoded_committers = None
269
        stats = repository.gather_stats(decoded_revision_id, decoded_committers)
270
271
        body = ''
272
        if stats.has_key('committers'):
273
            body += 'committers: %d\n' % stats['committers']
274
        if stats.has_key('firstrev'):
275
            body += 'firstrev: %.3f %d\n' % stats['firstrev']
276
        if stats.has_key('latestrev'):
277
             body += 'latestrev: %.3f %d\n' % stats['latestrev']
278
        if stats.has_key('revisions'):
279
            body += 'revisions: %d\n' % stats['revisions']
280
        if stats.has_key('size'):
281
            body += 'size: %d\n' % stats['size']
282
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
283
        return SuccessfulSmartServerResponse(('ok', ), body)
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
284
285
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
286
class SmartServerRepositoryIsShared(SmartServerRepositoryRequest):
287
288
    def do_repository_request(self, repository):
289
        """Return the result of repository.is_shared().
290
291
        :param repository: The repository to query in.
292
        :return: A smart server response of ('yes', ) if the repository is
293
            shared, and ('no', ) if it is not.
294
        """
295
        if repository.is_shared():
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
296
            return SuccessfulSmartServerResponse(('yes', ))
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
297
        else:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
298
            return SuccessfulSmartServerResponse(('no', ))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
299
300
301
class SmartServerRepositoryLockWrite(SmartServerRepositoryRequest):
302
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
303
    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
304
        # XXX: this probably should not have a token.
305
        if token == '':
306
            token = None
307
        try:
308
            token = repository.lock_write(token=token)
309
        except errors.LockContention, e:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
310
            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.
311
        except errors.UnlockableTransport:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
312
            return FailedSmartServerResponse(('UnlockableTransport',))
2872.5.3 by Martin Pool
Pass back LockFailed from smart server lock methods
313
        except errors.LockFailed, e:
314
            return FailedSmartServerResponse(('LockFailed',
315
                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.
316
        if token is not None:
317
            repository.leave_lock_in_place()
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
318
        repository.unlock()
319
        if token is None:
320
            token = ''
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
321
        return SuccessfulSmartServerResponse(('ok', token))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
322
323
324
class SmartServerRepositoryUnlock(SmartServerRepositoryRequest):
325
326
    def do_repository_request(self, repository, token):
327
        try:
328
            repository.lock_write(token=token)
329
        except errors.TokenMismatch, e:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
330
            return FailedSmartServerResponse(('TokenMismatch',))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
331
        repository.dont_leave_lock_in_place()
332
        repository.unlock()
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
333
        return SuccessfulSmartServerResponse(('ok',))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
334
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
335
336
class SmartServerRepositoryTarball(SmartServerRepositoryRequest):
2018.18.11 by Martin Pool
merge hpss changes
337
    """Get the raw repository files as a tarball.
338
339
    The returned tarball contains a .bzr control directory which in turn
340
    contains a repository.
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
341
    
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
342
    This takes one parameter, compression, which currently must be 
343
    "", "gz", or "bz2".
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
344
345
    This is used to implement the Repository.copy_content_into operation.
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
346
    """
347
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
348
    def do_repository_request(self, repository, compression):
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
349
        from bzrlib import osutils
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
350
        repo_transport = repository.control_files._transport
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
351
        tmp_dirname, tmp_repo = self._copy_to_tempdir(repository)
2018.18.5 by Martin Pool
Repository.tarball locks repository while running for consistency
352
        try:
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
353
            controldir_name = tmp_dirname + '/.bzr'
354
            return self._tarfile_response(controldir_name, compression)
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
355
        finally:
356
            osutils.rmtree(tmp_dirname)
357
358
    def _copy_to_tempdir(self, from_repo):
359
        tmp_dirname = tempfile.mkdtemp(prefix='tmpbzrclone')
360
        tmp_bzrdir = from_repo.bzrdir._format.initialize(tmp_dirname)
361
        tmp_repo = from_repo._format.initialize(tmp_bzrdir)
362
        from_repo.copy_content_into(tmp_repo)
363
        return tmp_dirname, tmp_repo
364
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
365
    def _tarfile_response(self, tmp_dirname, compression):
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
366
        temp = tempfile.NamedTemporaryFile()
367
        try:
2557.1.1 by Martin Pool
[BUG 119330] Fix tempfile permissions error in smart server tar bundling (under windows) (Martin_)
368
            self._tarball_of_dir(tmp_dirname, compression, temp.file)
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
369
            # all finished; write the tempfile out to the network
370
            temp.seek(0)
2420.2.2 by Andrew Bennetts
Merge tarball branch that's already with PQM, resolving conflicts.
371
            return SuccessfulSmartServerResponse(('ok',), temp.read())
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
372
            # FIXME: Don't read the whole thing into memory here; rather stream it
373
            # out from the file onto the network. mbp 20070411
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
374
        finally:
375
            temp.close()
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
376
2557.1.1 by Martin Pool
[BUG 119330] Fix tempfile permissions error in smart server tar bundling (under windows) (Martin_)
377
    def _tarball_of_dir(self, dirname, compression, ofile):
2571.2.2 by Ian Clatworthy
use basename as poolie recommended
378
        filename = os.path.basename(ofile.name)
379
        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
380
            mode='w|' + compression)
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
381
        try:
382
            # The tarball module only accepts ascii names, and (i guess)
383
            # packs them with their 8bit names.  We know all the files
384
            # within the repository have ASCII names so the should be safe
385
            # to pack in.
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
386
            dirname = dirname.encode(sys.getfilesystemencoding())
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
387
            # python's tarball module includes the whole path by default so
388
            # override it
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
389
            assert dirname.endswith('.bzr')
390
            tarball.add(dirname, '.bzr') # recursive by default
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
391
        finally:
392
            tarball.close()
2535.3.12 by Andrew Bennetts
Add a first cut of a get_data_stream method to Repository.
393
394
2535.3.49 by Andrew Bennetts
Rename 'Repository.fetch_revisions' smart request to 'Repository.stream_knit_data_for_revisions'.
395
class SmartServerRepositoryStreamKnitDataForRevisions(SmartServerRepositoryRequest):
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.
396
    """Bzr <= 1.1 streaming pull, buffers all data on server."""
2535.3.14 by Andrew Bennetts
Move serialising repo data stream to bytes into smart protocol.
397
398
    def do_repository_request(self, repository, *revision_ids):
3015.2.7 by Robert Collins
In the RemoteServer repository methods handle repositories that cannot be remotely locked like pack repositories, and add a read lock in SmartServerRepositoryStreamKnitDataForRevisions.
399
        repository.lock_read()
400
        try:
2535.4.29 by Andrew Bennetts
Add a new smart method, Repository.stream_revisions_chunked, rather than changing the behaviour of an existing method.
401
            return self._do_repository_request(repository, revision_ids)
402
        finally:
403
            repository.unlock()
404
405
    def _do_repository_request(self, repository, revision_ids):
3184.1.9 by Robert Collins
* ``Repository.get_data_stream`` is now deprecated in favour of
406
        stream = repository.get_data_stream_for_search(
407
            repository.revision_ids_to_search_result(set(revision_ids)))
2535.4.30 by Andrew Bennetts
Use ContainerSerialiser rather than ContainerWriter in bzrlib/smart/repository.py.
408
        buffer = StringIO()
409
        pack = ContainerSerialiser()
410
        buffer.write(pack.begin())
2535.4.29 by Andrew Bennetts
Add a new smart method, Repository.stream_revisions_chunked, rather than changing the behaviour of an existing method.
411
        try:
3194.1.1 by Andrew Bennetts
Fix one LockableFiles warning from test_smart.py
412
            try:
413
                for name_tuple, bytes in stream:
414
                    buffer.write(pack.bytes_record(bytes, [name_tuple]))
415
            except:
3194.1.3 by Andrew Bennetts
Change "that that" to just "that".
416
                # Undo the lock_read that happens once the iterator from
3194.1.1 by Andrew Bennetts
Fix one LockableFiles warning from test_smart.py
417
                # get_data_stream is started.
418
                repository.unlock()
419
                raise
2535.4.29 by Andrew Bennetts
Add a new smart method, Repository.stream_revisions_chunked, rather than changing the behaviour of an existing method.
420
        except errors.RevisionNotPresent, e:
421
            return FailedSmartServerResponse(('NoSuchRevision', e.revision_id))
2535.4.30 by Andrew Bennetts
Use ContainerSerialiser rather than ContainerWriter in bzrlib/smart/repository.py.
422
        buffer.write(pack.end())
423
        return SuccessfulSmartServerResponse(('ok',), buffer.getvalue())
2535.4.29 by Andrew Bennetts
Add a new smart method, Repository.stream_revisions_chunked, rather than changing the behaviour of an existing method.
424
425
426
class SmartServerRepositoryStreamRevisionsChunked(SmartServerRepositoryRequest):
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.
427
    """Bzr 1.1+ streaming pull."""
2535.4.29 by Andrew Bennetts
Add a new smart method, Repository.stream_revisions_chunked, rather than changing the behaviour of an existing method.
428
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.
429
    def do_body(self, body_bytes):
430
        repository = self._repository
2535.4.29 by Andrew Bennetts
Add a new smart method, Repository.stream_revisions_chunked, rather than changing the behaviour of an existing method.
431
        repository.lock_read()
432
        try:
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.
433
            search, error = self.recreate_search(repository, body_bytes)
434
            if error is not None:
3194.1.2 by Andrew Bennetts
Fix another LockableFiles not unlocked warning from test_smart.
435
                repository.unlock()
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.
436
                return error
437
            stream = repository.get_data_stream_for_search(search.get_result())
2535.4.26 by Andrew Bennetts
Fix locking problem that was hanging the tests.
438
        except Exception:
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.
439
            # On non-error, unlocking is done by the body stream handler.
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.
440
            repository.unlock()
2535.4.26 by Andrew Bennetts
Fix locking problem that was hanging the tests.
441
            raise
2535.4.2 by Andrew Bennetts
Nasty hackery to make stream_knit_data_for_revisions response use streaming.
442
        return SuccessfulSmartServerResponse(('ok',),
2535.4.26 by Andrew Bennetts
Fix locking problem that was hanging the tests.
443
            body_stream=self.body_stream(stream, repository))
2535.4.2 by Andrew Bennetts
Nasty hackery to make stream_knit_data_for_revisions response use streaming.
444
2535.4.26 by Andrew Bennetts
Fix locking problem that was hanging the tests.
445
    def body_stream(self, stream, repository):
2535.4.18 by Andrew Bennetts
Use pack.ContainerSerialiser to remove some nasty cruft.
446
        pack = ContainerSerialiser()
447
        yield pack.begin()
2535.3.40 by Andrew Bennetts
Tidy up more XXXs.
448
        try:
2535.3.50 by Andrew Bennetts
Use tuple names in data streams rather than concatenated strings.
449
            for name_tuple, bytes in stream:
2535.4.18 by Andrew Bennetts
Use pack.ContainerSerialiser to remove some nasty cruft.
450
                yield pack.bytes_record(bytes, [name_tuple])
2535.3.40 by Andrew Bennetts
Tidy up more XXXs.
451
        except errors.RevisionNotPresent, e:
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.
452
            # This shouldn't be able to happen, but as we don't buffer
453
            # everything it can in theory happen.
2535.4.5 by Andrew Bennetts
Merge latest chunking protocol, including support for errors, fixing a test failure.
454
            yield FailedSmartServerResponse(('NoSuchRevision', e.revision_id))
2535.4.26 by Andrew Bennetts
Fix locking problem that was hanging the tests.
455
        repository.unlock()
2535.3.14 by Andrew Bennetts
Move serialising repo data stream to bytes into smart protocol.
456
        pack.end()
2535.3.15 by Andrew Bennetts
Add KnitVersionedFile.get_stream_as_bytes, start smart implementation of RemoteRepository.get_data_stream.
457