/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/branch.py

  • Committer: Jelmer Vernooij
  • Date: 2011-12-18 15:28:38 UTC
  • mto: This revision was merged to the branch mainline in revision 6386.
  • Revision ID: jelmer@samba.org-20111218152838-5wxpfnugk2jd625k
UseĀ absolute_import.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006-2010 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
from __future__ import absolute_import
 
18
 
 
19
"""Server-side branch related request implmentations."""
 
20
 
 
21
 
 
22
from bzrlib import (
 
23
    bencode,
 
24
    errors,
 
25
    revision as _mod_revision,
 
26
    )
 
27
from bzrlib.controldir import ControlDir
 
28
from bzrlib.smart.request import (
 
29
    FailedSmartServerResponse,
 
30
    SmartServerRequest,
 
31
    SuccessfulSmartServerResponse,
 
32
    )
 
33
 
 
34
 
 
35
class SmartServerBranchRequest(SmartServerRequest):
 
36
    """Base class for handling common branch request logic.
 
37
    """
 
38
 
 
39
    def do(self, path, *args):
 
40
        """Execute a request for a branch at path.
 
41
 
 
42
        All Branch requests take a path to the branch as their first argument.
 
43
 
 
44
        If the branch is a branch reference, NotBranchError is raised.
 
45
 
 
46
        :param path: The path for the repository as received from the
 
47
            client.
 
48
        :return: A SmartServerResponse from self.do_with_branch().
 
49
        """
 
50
        transport = self.transport_from_client_path(path)
 
51
        controldir = ControlDir.open_from_transport(transport)
 
52
        if controldir.get_branch_reference() is not None:
 
53
            raise errors.NotBranchError(transport.base)
 
54
        branch = controldir.open_branch(ignore_fallbacks=True)
 
55
        return self.do_with_branch(branch, *args)
 
56
 
 
57
 
 
58
class SmartServerLockedBranchRequest(SmartServerBranchRequest):
 
59
    """Base class for handling common branch request logic for requests that
 
60
    need a write lock.
 
61
    """
 
62
 
 
63
    def do_with_branch(self, branch, branch_token, repo_token, *args):
 
64
        """Execute a request for a branch.
 
65
 
 
66
        A write lock will be acquired with the given tokens for the branch and
 
67
        repository locks.  The lock will be released once the request is
 
68
        processed.  The physical lock state won't be changed.
 
69
        """
 
70
        # XXX: write a test for LockContention
 
71
        branch.repository.lock_write(token=repo_token)
 
72
        try:
 
73
            branch.lock_write(token=branch_token)
 
74
            try:
 
75
                return self.do_with_locked_branch(branch, *args)
 
76
            finally:
 
77
                branch.unlock()
 
78
        finally:
 
79
            branch.repository.unlock()
 
80
 
 
81
 
 
82
class SmartServerBranchBreakLock(SmartServerBranchRequest):
 
83
 
 
84
    def do_with_branch(self, branch):
 
85
        """Break a branch lock.
 
86
        """
 
87
        branch.break_lock()
 
88
        return SuccessfulSmartServerResponse(('ok', ), )
 
89
 
 
90
 
 
91
class SmartServerBranchGetConfigFile(SmartServerBranchRequest):
 
92
 
 
93
    def do_with_branch(self, branch):
 
94
        """Return the content of branch.conf
 
95
 
 
96
        The body is not utf8 decoded - its the literal bytestream from disk.
 
97
        """
 
98
        try:
 
99
            content = branch.control_transport.get_bytes('branch.conf')
 
100
        except errors.NoSuchFile:
 
101
            content = ''
 
102
        return SuccessfulSmartServerResponse( ('ok', ), content)
 
103
 
 
104
 
 
105
class SmartServerBranchPutConfigFile(SmartServerBranchRequest):
 
106
    """Set the configuration data for a branch.
 
107
 
 
108
    New in 2.5.
 
109
    """
 
110
 
 
111
    def do_with_branch(self, branch, branch_token, repo_token):
 
112
        """Set the content of branch.conf.
 
113
 
 
114
        The body is not utf8 decoded - its the literal bytestream for disk.
 
115
        """
 
116
        self._branch = branch
 
117
        self._branch_token = branch_token
 
118
        self._repo_token = repo_token
 
119
        # Signal we want a body
 
120
        return None
 
121
 
 
122
    def do_body(self, body_bytes):
 
123
        self._branch.repository.lock_write(token=self._repo_token)
 
124
        try:
 
125
            self._branch.lock_write(token=self._branch_token)
 
126
            try:
 
127
                self._branch.control_transport.put_bytes(
 
128
                    'branch.conf', body_bytes)
 
129
            finally:
 
130
                self._branch.unlock()
 
131
        finally:
 
132
            self._branch.repository.unlock()
 
133
        return SuccessfulSmartServerResponse(('ok', ))
 
134
 
 
135
 
 
136
class SmartServerBranchGetParent(SmartServerBranchRequest):
 
137
 
 
138
    def do_with_branch(self, branch):
 
139
        """Return the parent of branch."""
 
140
        parent = branch._get_parent_location() or ''
 
141
        return SuccessfulSmartServerResponse((parent,))
 
142
 
 
143
 
 
144
class SmartServerBranchGetTagsBytes(SmartServerBranchRequest):
 
145
 
 
146
    def do_with_branch(self, branch):
 
147
        """Return the _get_tags_bytes for a branch."""
 
148
        bytes = branch._get_tags_bytes()
 
149
        return SuccessfulSmartServerResponse((bytes,))
 
150
 
 
151
 
 
152
class SmartServerBranchSetTagsBytes(SmartServerLockedBranchRequest):
 
153
 
 
154
    def __init__(self, backing_transport, root_client_path='/', jail_root=None):
 
155
        SmartServerLockedBranchRequest.__init__(
 
156
            self, backing_transport, root_client_path, jail_root)
 
157
        self.locked = False
 
158
        
 
159
    def do_with_locked_branch(self, branch):
 
160
        """Call _set_tags_bytes for a branch.
 
161
 
 
162
        New in 1.18.
 
163
        """
 
164
        # We need to keep this branch locked until we get a body with the tags
 
165
        # bytes.
 
166
        self.branch = branch
 
167
        self.branch.lock_write()
 
168
        self.locked = True
 
169
 
 
170
    def do_body(self, bytes):
 
171
        self.branch._set_tags_bytes(bytes)
 
172
        return SuccessfulSmartServerResponse(())
 
173
 
 
174
    def do_end(self):
 
175
        # TODO: this request shouldn't have to do this housekeeping manually.
 
176
        # Some of this logic probably belongs in a base class.
 
177
        if not self.locked:
 
178
            # We never acquired the branch successfully in the first place, so
 
179
            # there's nothing more to do.
 
180
            return
 
181
        try:
 
182
            return SmartServerLockedBranchRequest.do_end(self)
 
183
        finally:
 
184
            # Only try unlocking if we locked successfully in the first place
 
185
            self.branch.unlock()
 
186
 
 
187
 
 
188
class SmartServerBranchHeadsToFetch(SmartServerBranchRequest):
 
189
 
 
190
    def do_with_branch(self, branch):
 
191
        """Return the heads-to-fetch for a Branch as two bencoded lists.
 
192
        
 
193
        See Branch.heads_to_fetch.
 
194
 
 
195
        New in 2.4.
 
196
        """
 
197
        must_fetch, if_present_fetch = branch.heads_to_fetch()
 
198
        return SuccessfulSmartServerResponse(
 
199
            (list(must_fetch), list(if_present_fetch)))
 
200
 
 
201
 
 
202
class SmartServerBranchRequestGetStackedOnURL(SmartServerBranchRequest):
 
203
 
 
204
    def do_with_branch(self, branch):
 
205
        stacked_on_url = branch.get_stacked_on_url()
 
206
        return SuccessfulSmartServerResponse(('ok', stacked_on_url))
 
207
 
 
208
 
 
209
class SmartServerRequestRevisionHistory(SmartServerBranchRequest):
 
210
 
 
211
    def do_with_branch(self, branch):
 
212
        """Get the revision history for the branch.
 
213
 
 
214
        The revision list is returned as the body content,
 
215
        with each revision utf8 encoded and \x00 joined.
 
216
        """
 
217
        branch.lock_read()
 
218
        try:
 
219
            graph = branch.repository.get_graph()
 
220
            stop_revisions = (None, _mod_revision.NULL_REVISION)
 
221
            history = list(graph.iter_lefthand_ancestry(
 
222
                branch.last_revision(), stop_revisions))
 
223
        finally:
 
224
            branch.unlock()
 
225
        return SuccessfulSmartServerResponse(
 
226
            ('ok', ), ('\x00'.join(reversed(history))))
 
227
 
 
228
 
 
229
class SmartServerBranchRequestLastRevisionInfo(SmartServerBranchRequest):
 
230
 
 
231
    def do_with_branch(self, branch):
 
232
        """Return branch.last_revision_info().
 
233
 
 
234
        The revno is encoded in decimal, the revision_id is encoded as utf8.
 
235
        """
 
236
        revno, last_revision = branch.last_revision_info()
 
237
        return SuccessfulSmartServerResponse(('ok', str(revno), last_revision))
 
238
 
 
239
 
 
240
class SmartServerBranchRequestRevisionIdToRevno(SmartServerBranchRequest):
 
241
 
 
242
    def do_with_branch(self, branch, revid):
 
243
        """Return branch.revision_id_to_revno().
 
244
 
 
245
        New in 2.5.
 
246
 
 
247
        The revno is encoded in decimal, the revision_id is encoded as utf8.
 
248
        """
 
249
        try:
 
250
            dotted_revno = branch.revision_id_to_dotted_revno(revid)
 
251
        except errors.NoSuchRevision:
 
252
            return FailedSmartServerResponse(('NoSuchRevision', revid))
 
253
        return SuccessfulSmartServerResponse(
 
254
            ('ok', ) + tuple(map(str, dotted_revno)))
 
255
 
 
256
 
 
257
class SmartServerSetTipRequest(SmartServerLockedBranchRequest):
 
258
    """Base class for handling common branch request logic for requests that
 
259
    update the branch tip.
 
260
    """
 
261
 
 
262
    def do_with_locked_branch(self, branch, *args):
 
263
        try:
 
264
            return self.do_tip_change_with_locked_branch(branch, *args)
 
265
        except errors.TipChangeRejected, e:
 
266
            msg = e.msg
 
267
            if isinstance(msg, unicode):
 
268
                msg = msg.encode('utf-8')
 
269
            return FailedSmartServerResponse(('TipChangeRejected', msg))
 
270
 
 
271
 
 
272
class SmartServerBranchRequestSetConfigOption(SmartServerLockedBranchRequest):
 
273
    """Set an option in the branch configuration."""
 
274
 
 
275
    def do_with_locked_branch(self, branch, value, name, section):
 
276
        if not section:
 
277
            section = None
 
278
        branch._get_config().set_option(value.decode('utf8'), name, section)
 
279
        return SuccessfulSmartServerResponse(())
 
280
 
 
281
 
 
282
class SmartServerBranchRequestSetConfigOptionDict(SmartServerLockedBranchRequest):
 
283
    """Set an option in the branch configuration.
 
284
    
 
285
    New in 2.2.
 
286
    """
 
287
 
 
288
    def do_with_locked_branch(self, branch, value_dict, name, section):
 
289
        utf8_dict = bencode.bdecode(value_dict)
 
290
        value_dict = {}
 
291
        for key, value in utf8_dict.items():
 
292
            value_dict[key.decode('utf8')] = value.decode('utf8')
 
293
        if not section:
 
294
            section = None
 
295
        branch._get_config().set_option(value_dict, name, section)
 
296
        return SuccessfulSmartServerResponse(())
 
297
 
 
298
 
 
299
class SmartServerBranchRequestSetLastRevision(SmartServerSetTipRequest):
 
300
 
 
301
    def do_tip_change_with_locked_branch(self, branch, new_last_revision_id):
 
302
        if new_last_revision_id == 'null:':
 
303
            branch._set_revision_history([])
 
304
        else:
 
305
            if not branch.repository.has_revision(new_last_revision_id):
 
306
                return FailedSmartServerResponse(
 
307
                    ('NoSuchRevision', new_last_revision_id))
 
308
            branch._set_revision_history(branch._lefthand_history(
 
309
                new_last_revision_id, None, None))
 
310
        return SuccessfulSmartServerResponse(('ok',))
 
311
 
 
312
 
 
313
class SmartServerBranchRequestSetLastRevisionEx(SmartServerSetTipRequest):
 
314
 
 
315
    def do_tip_change_with_locked_branch(self, branch, new_last_revision_id,
 
316
            allow_divergence, allow_overwrite_descendant):
 
317
        """Set the last revision of the branch.
 
318
 
 
319
        New in 1.6.
 
320
 
 
321
        :param new_last_revision_id: the revision ID to set as the last
 
322
            revision of the branch.
 
323
        :param allow_divergence: A flag.  If non-zero, change the revision ID
 
324
            even if the new_last_revision_id's ancestry has diverged from the
 
325
            current last revision.  If zero, a 'Diverged' error will be
 
326
            returned if new_last_revision_id is not a descendant of the current
 
327
            last revision.
 
328
        :param allow_overwrite_descendant:  A flag.  If zero and
 
329
            new_last_revision_id is not a descendant of the current last
 
330
            revision, then the last revision will not be changed.  If non-zero
 
331
            and there is no divergence, then the last revision is always
 
332
            changed.
 
333
 
 
334
        :returns: on success, a tuple of ('ok', revno, revision_id), where
 
335
            revno and revision_id are the new values of the current last
 
336
            revision info.  The revision_id might be different to the
 
337
            new_last_revision_id if allow_overwrite_descendant was not set.
 
338
        """
 
339
        do_not_overwrite_descendant = not allow_overwrite_descendant
 
340
        try:
 
341
            last_revno, last_rev = branch.last_revision_info()
 
342
            graph = branch.repository.get_graph()
 
343
            if not allow_divergence or do_not_overwrite_descendant:
 
344
                relation = branch._revision_relations(
 
345
                    last_rev, new_last_revision_id, graph)
 
346
                if relation == 'diverged' and not allow_divergence:
 
347
                    return FailedSmartServerResponse(('Diverged',))
 
348
                if relation == 'a_descends_from_b' and do_not_overwrite_descendant:
 
349
                    return SuccessfulSmartServerResponse(
 
350
                        ('ok', last_revno, last_rev))
 
351
            new_revno = graph.find_distance_to_null(
 
352
                new_last_revision_id, [(last_rev, last_revno)])
 
353
            branch.set_last_revision_info(new_revno, new_last_revision_id)
 
354
        except errors.GhostRevisionsHaveNoRevno:
 
355
            return FailedSmartServerResponse(
 
356
                ('NoSuchRevision', new_last_revision_id))
 
357
        return SuccessfulSmartServerResponse(
 
358
            ('ok', new_revno, new_last_revision_id))
 
359
 
 
360
 
 
361
class SmartServerBranchRequestSetLastRevisionInfo(SmartServerSetTipRequest):
 
362
    """Branch.set_last_revision_info.  Sets the revno and the revision ID of
 
363
    the specified branch.
 
364
 
 
365
    New in bzrlib 1.4.
 
366
    """
 
367
 
 
368
    def do_tip_change_with_locked_branch(self, branch, new_revno,
 
369
            new_last_revision_id):
 
370
        try:
 
371
            branch.set_last_revision_info(int(new_revno), new_last_revision_id)
 
372
        except errors.NoSuchRevision:
 
373
            return FailedSmartServerResponse(
 
374
                ('NoSuchRevision', new_last_revision_id))
 
375
        return SuccessfulSmartServerResponse(('ok',))
 
376
 
 
377
 
 
378
class SmartServerBranchRequestSetParentLocation(SmartServerLockedBranchRequest):
 
379
    """Set the parent location for a branch.
 
380
    
 
381
    Takes a location to set, which must be utf8 encoded.
 
382
    """
 
383
 
 
384
    def do_with_locked_branch(self, branch, location):
 
385
        branch._set_parent_location(location)
 
386
        return SuccessfulSmartServerResponse(())
 
387
 
 
388
 
 
389
class SmartServerBranchRequestLockWrite(SmartServerBranchRequest):
 
390
 
 
391
    def do_with_branch(self, branch, branch_token='', repo_token=''):
 
392
        if branch_token == '':
 
393
            branch_token = None
 
394
        if repo_token == '':
 
395
            repo_token = None
 
396
        try:
 
397
            repo_token = branch.repository.lock_write(
 
398
                token=repo_token).repository_token
 
399
            try:
 
400
                branch_token = branch.lock_write(
 
401
                    token=branch_token).branch_token
 
402
            finally:
 
403
                # this leaves the repository with 1 lock
 
404
                branch.repository.unlock()
 
405
        except errors.LockContention:
 
406
            return FailedSmartServerResponse(('LockContention',))
 
407
        except errors.TokenMismatch:
 
408
            return FailedSmartServerResponse(('TokenMismatch',))
 
409
        except errors.UnlockableTransport:
 
410
            return FailedSmartServerResponse(('UnlockableTransport',))
 
411
        except errors.LockFailed, e:
 
412
            return FailedSmartServerResponse(('LockFailed', str(e.lock), str(e.why)))
 
413
        if repo_token is None:
 
414
            repo_token = ''
 
415
        else:
 
416
            branch.repository.leave_lock_in_place()
 
417
        branch.leave_lock_in_place()
 
418
        branch.unlock()
 
419
        return SuccessfulSmartServerResponse(('ok', branch_token, repo_token))
 
420
 
 
421
 
 
422
class SmartServerBranchRequestUnlock(SmartServerBranchRequest):
 
423
 
 
424
    def do_with_branch(self, branch, branch_token, repo_token):
 
425
        try:
 
426
            branch.repository.lock_write(token=repo_token)
 
427
            try:
 
428
                branch.lock_write(token=branch_token)
 
429
            finally:
 
430
                branch.repository.unlock()
 
431
        except errors.TokenMismatch:
 
432
            return FailedSmartServerResponse(('TokenMismatch',))
 
433
        if repo_token:
 
434
            branch.repository.dont_leave_lock_in_place()
 
435
        branch.dont_leave_lock_in_place()
 
436
        branch.unlock()
 
437
        return SuccessfulSmartServerResponse(('ok',))
 
438
 
 
439
 
 
440
class SmartServerBranchRequestGetPhysicalLockStatus(SmartServerBranchRequest):
 
441
    """Get the physical lock status for a branch.
 
442
 
 
443
    New in 2.5.
 
444
    """
 
445
 
 
446
    def do_with_branch(self, branch):
 
447
        if branch.get_physical_lock_status():
 
448
            return SuccessfulSmartServerResponse(('yes',))
 
449
        else:
 
450
            return SuccessfulSmartServerResponse(('no',))