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