/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/smart/repository.py

  • Committer: Martin Pool
  • Date: 2007-10-12 08:00:07 UTC
  • mto: This revision was merged to the branch mainline in revision 2913.
  • Revision ID: mbp@sourcefrog.net-20071012080007-vf80woayyom8s8e1
Rename update_to_one_parent_via_delta to more wieldy update_basis_by_delta

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006, 2007 Canonical Ltd
 
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
 
 
19
import os
 
20
import sys
 
21
import tempfile
 
22
import tarfile
 
23
 
 
24
from bzrlib import errors
 
25
from bzrlib.bzrdir import BzrDir
 
26
from bzrlib.smart.request import (
 
27
    FailedSmartServerResponse,
 
28
    SmartServerRequest,
 
29
    SuccessfulSmartServerResponse,
 
30
    )
 
31
 
 
32
 
 
33
class SmartServerRepositoryRequest(SmartServerRequest):
 
34
    """Common base class for Repository requests."""
 
35
 
 
36
    def do(self, path, *args):
 
37
        """Execute a repository request.
 
38
        
 
39
        The repository must be at the exact path - no searching is done.
 
40
 
 
41
        The actual logic is delegated to self.do_repository_request.
 
42
 
 
43
        :param path: The path for the repository.
 
44
        :return: A smart server from self.do_repository_request().
 
45
        """
 
46
        transport = self._backing_transport.clone(path)
 
47
        bzrdir = BzrDir.open_from_transport(transport)
 
48
        repository = bzrdir.open_repository()
 
49
        return self.do_repository_request(repository, *args)
 
50
 
 
51
 
 
52
class SmartServerRepositoryGetRevisionGraph(SmartServerRepositoryRequest):
 
53
    
 
54
    def do_repository_request(self, repository, revision_id):
 
55
        """Return the result of repository.get_revision_graph(revision_id).
 
56
        
 
57
        :param repository: The repository to query in.
 
58
        :param revision_id: The utf8 encoded revision_id to get a graph from.
 
59
        :return: A smart server response where the body contains an utf8
 
60
            encoded flattened list of the revision graph.
 
61
        """
 
62
        if not revision_id:
 
63
            revision_id = None
 
64
 
 
65
        lines = []
 
66
        try:
 
67
            revision_graph = repository.get_revision_graph(revision_id)
 
68
        except errors.NoSuchRevision:
 
69
            # Note that we return an empty body, rather than omitting the body.
 
70
            # This way the client knows that it can always expect to find a body
 
71
            # in the response for this method, even in the error case.
 
72
            return FailedSmartServerResponse(('nosuchrevision', revision_id), '')
 
73
 
 
74
        for revision, parents in revision_graph.items():
 
75
            lines.append(' '.join((revision, ) + tuple(parents)))
 
76
 
 
77
        return SuccessfulSmartServerResponse(('ok', ), '\n'.join(lines))
 
78
 
 
79
 
 
80
class SmartServerRequestHasRevision(SmartServerRepositoryRequest):
 
81
 
 
82
    def do_repository_request(self, repository, revision_id):
 
83
        """Return ok if a specific revision is in the repository at path.
 
84
 
 
85
        :param repository: The repository to query in.
 
86
        :param revision_id: The utf8 encoded revision_id to lookup.
 
87
        :return: A smart server response of ('ok', ) if the revision is
 
88
            present.
 
89
        """
 
90
        if repository.has_revision(revision_id):
 
91
            return SuccessfulSmartServerResponse(('yes', ))
 
92
        else:
 
93
            return SuccessfulSmartServerResponse(('no', ))
 
94
 
 
95
 
 
96
class SmartServerRepositoryGatherStats(SmartServerRepositoryRequest):
 
97
 
 
98
    def do_repository_request(self, repository, revid, committers):
 
99
        """Return the result of repository.gather_stats().
 
100
 
 
101
        :param repository: The repository to query in.
 
102
        :param revid: utf8 encoded rev id or an empty string to indicate None
 
103
        :param committers: 'yes' or 'no'.
 
104
 
 
105
        :return: A SmartServerResponse ('ok',), a encoded body looking like
 
106
              committers: 1
 
107
              firstrev: 1234.230 0
 
108
              latestrev: 345.700 3600
 
109
              revisions: 2
 
110
              size:45
 
111
 
 
112
              But containing only fields returned by the gather_stats() call
 
113
        """
 
114
        if revid == '':
 
115
            decoded_revision_id = None
 
116
        else:
 
117
            decoded_revision_id = revid
 
118
        if committers == 'yes':
 
119
            decoded_committers = True
 
120
        else:
 
121
            decoded_committers = None
 
122
        stats = repository.gather_stats(decoded_revision_id, decoded_committers)
 
123
 
 
124
        body = ''
 
125
        if stats.has_key('committers'):
 
126
            body += 'committers: %d\n' % stats['committers']
 
127
        if stats.has_key('firstrev'):
 
128
            body += 'firstrev: %.3f %d\n' % stats['firstrev']
 
129
        if stats.has_key('latestrev'):
 
130
             body += 'latestrev: %.3f %d\n' % stats['latestrev']
 
131
        if stats.has_key('revisions'):
 
132
            body += 'revisions: %d\n' % stats['revisions']
 
133
        if stats.has_key('size'):
 
134
            body += 'size: %d\n' % stats['size']
 
135
 
 
136
        return SuccessfulSmartServerResponse(('ok', ), body)
 
137
 
 
138
 
 
139
class SmartServerRepositoryIsShared(SmartServerRepositoryRequest):
 
140
 
 
141
    def do_repository_request(self, repository):
 
142
        """Return the result of repository.is_shared().
 
143
 
 
144
        :param repository: The repository to query in.
 
145
        :return: A smart server response of ('yes', ) if the repository is
 
146
            shared, and ('no', ) if it is not.
 
147
        """
 
148
        if repository.is_shared():
 
149
            return SuccessfulSmartServerResponse(('yes', ))
 
150
        else:
 
151
            return SuccessfulSmartServerResponse(('no', ))
 
152
 
 
153
 
 
154
class SmartServerRepositoryLockWrite(SmartServerRepositoryRequest):
 
155
 
 
156
    def do_repository_request(self, repository, token=''):
 
157
        # XXX: this probably should not have a token.
 
158
        if token == '':
 
159
            token = None
 
160
        try:
 
161
            token = repository.lock_write(token=token)
 
162
        except errors.LockContention, e:
 
163
            return FailedSmartServerResponse(('LockContention',))
 
164
        except errors.UnlockableTransport:
 
165
            return FailedSmartServerResponse(('UnlockableTransport',))
 
166
        except errors.LockFailed, e:
 
167
            return FailedSmartServerResponse(('LockFailed',
 
168
                str(e.lock), str(e.why)))
 
169
        repository.leave_lock_in_place()
 
170
        repository.unlock()
 
171
        if token is None:
 
172
            token = ''
 
173
        return SuccessfulSmartServerResponse(('ok', token))
 
174
 
 
175
 
 
176
class SmartServerRepositoryUnlock(SmartServerRepositoryRequest):
 
177
 
 
178
    def do_repository_request(self, repository, token):
 
179
        try:
 
180
            repository.lock_write(token=token)
 
181
        except errors.TokenMismatch, e:
 
182
            return FailedSmartServerResponse(('TokenMismatch',))
 
183
        repository.dont_leave_lock_in_place()
 
184
        repository.unlock()
 
185
        return SuccessfulSmartServerResponse(('ok',))
 
186
 
 
187
 
 
188
class SmartServerRepositoryTarball(SmartServerRepositoryRequest):
 
189
    """Get the raw repository files as a tarball.
 
190
 
 
191
    The returned tarball contains a .bzr control directory which in turn
 
192
    contains a repository.
 
193
    
 
194
    This takes one parameter, compression, which currently must be 
 
195
    "", "gz", or "bz2".
 
196
 
 
197
    This is used to implement the Repository.copy_content_into operation.
 
198
    """
 
199
 
 
200
    def do_repository_request(self, repository, compression):
 
201
        from bzrlib import osutils
 
202
        repo_transport = repository.control_files._transport
 
203
        tmp_dirname, tmp_repo = self._copy_to_tempdir(repository)
 
204
        try:
 
205
            controldir_name = tmp_dirname + '/.bzr'
 
206
            return self._tarfile_response(controldir_name, compression)
 
207
        finally:
 
208
            osutils.rmtree(tmp_dirname)
 
209
 
 
210
    def _copy_to_tempdir(self, from_repo):
 
211
        tmp_dirname = tempfile.mkdtemp(prefix='tmpbzrclone')
 
212
        tmp_bzrdir = from_repo.bzrdir._format.initialize(tmp_dirname)
 
213
        tmp_repo = from_repo._format.initialize(tmp_bzrdir)
 
214
        from_repo.copy_content_into(tmp_repo)
 
215
        return tmp_dirname, tmp_repo
 
216
 
 
217
    def _tarfile_response(self, tmp_dirname, compression):
 
218
        temp = tempfile.NamedTemporaryFile()
 
219
        try:
 
220
            self._tarball_of_dir(tmp_dirname, compression, temp.file)
 
221
            # all finished; write the tempfile out to the network
 
222
            temp.seek(0)
 
223
            return SuccessfulSmartServerResponse(('ok',), temp.read())
 
224
            # FIXME: Don't read the whole thing into memory here; rather stream it
 
225
            # out from the file onto the network. mbp 20070411
 
226
        finally:
 
227
            temp.close()
 
228
 
 
229
    def _tarball_of_dir(self, dirname, compression, ofile):
 
230
        filename = os.path.basename(ofile.name)
 
231
        tarball = tarfile.open(fileobj=ofile, name=filename,
 
232
            mode='w|' + compression)
 
233
        try:
 
234
            # The tarball module only accepts ascii names, and (i guess)
 
235
            # packs them with their 8bit names.  We know all the files
 
236
            # within the repository have ASCII names so the should be safe
 
237
            # to pack in.
 
238
            dirname = dirname.encode(sys.getfilesystemencoding())
 
239
            # python's tarball module includes the whole path by default so
 
240
            # override it
 
241
            assert dirname.endswith('.bzr')
 
242
            tarball.add(dirname, '.bzr') # recursive by default
 
243
        finally:
 
244
            tarball.close()