~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/branch.py

(igc) What's New in Bazaar 2.1 document

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006 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
"""Server-side branch related request implmentations."""
 
18
 
 
19
 
 
20
from bzrlib import errors
 
21
from bzrlib.bzrdir import BzrDir
 
22
from bzrlib.smart.request import (
 
23
    FailedSmartServerResponse,
 
24
    SmartServerRequest,
 
25
    SuccessfulSmartServerResponse,
 
26
    )
 
27
 
 
28
 
 
29
class SmartServerBranchRequest(SmartServerRequest):
 
30
    """Base class for handling common branch request logic.
 
31
    """
 
32
 
 
33
    def do(self, path, *args):
 
34
        """Execute a request for a branch at path.
 
35
 
 
36
        All Branch requests take a path to the branch as their first argument.
 
37
 
 
38
        If the branch is a branch reference, NotBranchError is raised.
 
39
 
 
40
        :param path: The path for the repository as received from the
 
41
            client.
 
42
        :return: A SmartServerResponse from self.do_with_branch().
 
43
        """
 
44
        transport = self.transport_from_client_path(path)
 
45
        bzrdir = BzrDir.open_from_transport(transport)
 
46
        if bzrdir.get_branch_reference() is not None:
 
47
            raise errors.NotBranchError(transport.base)
 
48
        branch = bzrdir.open_branch(ignore_fallbacks=True)
 
49
        return self.do_with_branch(branch, *args)
 
50
 
 
51
 
 
52
class SmartServerLockedBranchRequest(SmartServerBranchRequest):
 
53
    """Base class for handling common branch request logic for requests that
 
54
    need a write lock.
 
55
    """
 
56
 
 
57
    def do_with_branch(self, branch, branch_token, repo_token, *args):
 
58
        """Execute a request for a branch.
 
59
 
 
60
        A write lock will be acquired with the given tokens for the branch and
 
61
        repository locks.  The lock will be released once the request is
 
62
        processed.  The physical lock state won't be changed.
 
63
        """
 
64
        # XXX: write a test for LockContention
 
65
        branch.repository.lock_write(token=repo_token)
 
66
        try:
 
67
            branch.lock_write(token=branch_token)
 
68
            try:
 
69
                return self.do_with_locked_branch(branch, *args)
 
70
            finally:
 
71
                branch.unlock()
 
72
        finally:
 
73
            branch.repository.unlock()
 
74
 
 
75
 
 
76
class SmartServerBranchGetConfigFile(SmartServerBranchRequest):
 
77
 
 
78
    def do_with_branch(self, branch):
 
79
        """Return the content of branch.conf
 
80
 
 
81
        The body is not utf8 decoded - its the literal bytestream from disk.
 
82
        """
 
83
        try:
 
84
            content = branch._transport.get_bytes('branch.conf')
 
85
        except errors.NoSuchFile:
 
86
            content = ''
 
87
        return SuccessfulSmartServerResponse( ('ok', ), content)
 
88
 
 
89
 
 
90
class SmartServerBranchGetParent(SmartServerBranchRequest):
 
91
 
 
92
    def do_with_branch(self, branch):
 
93
        """Return the parent of branch."""
 
94
        parent = branch._get_parent_location() or ''
 
95
        return SuccessfulSmartServerResponse((parent,))
 
96
 
 
97
 
 
98
class SmartServerBranchGetTagsBytes(SmartServerBranchRequest):
 
99
 
 
100
    def do_with_branch(self, branch):
 
101
        """Return the _get_tags_bytes for a branch."""
 
102
        bytes = branch._get_tags_bytes()
 
103
        return SuccessfulSmartServerResponse((bytes,))
 
104
 
 
105
 
 
106
class SmartServerBranchSetTagsBytes(SmartServerLockedBranchRequest):
 
107
 
 
108
    def __init__(self, backing_transport, root_client_path='/', jail_root=None):
 
109
        SmartServerLockedBranchRequest.__init__(
 
110
            self, backing_transport, root_client_path, jail_root)
 
111
        self.locked = False
 
112
        
 
113
    def do_with_locked_branch(self, branch):
 
114
        """Call _set_tags_bytes for a branch.
 
115
 
 
116
        New in 1.18.
 
117
        """
 
118
        # We need to keep this branch locked until we get a body with the tags
 
119
        # bytes.
 
120
        self.branch = branch
 
121
        self.branch.lock_write()
 
122
        self.locked = True
 
123
 
 
124
    def do_body(self, bytes):
 
125
        self.branch._set_tags_bytes(bytes)
 
126
        return SuccessfulSmartServerResponse(())
 
127
 
 
128
    def do_end(self):
 
129
        # TODO: this request shouldn't have to do this housekeeping manually.
 
130
        # Some of this logic probably belongs in a base class.
 
131
        if not self.locked:
 
132
            # We never acquired the branch successfully in the first place, so
 
133
            # there's nothing more to do.
 
134
            return
 
135
        try:
 
136
            return SmartServerLockedBranchRequest.do_end(self)
 
137
        finally:
 
138
            # Only try unlocking if we locked successfully in the first place
 
139
            self.branch.unlock()
 
140
 
 
141
 
 
142
class SmartServerBranchRequestGetStackedOnURL(SmartServerBranchRequest):
 
143
 
 
144
    def do_with_branch(self, branch):
 
145
        stacked_on_url = branch.get_stacked_on_url()
 
146
        return SuccessfulSmartServerResponse(('ok', stacked_on_url))
 
147
 
 
148
 
 
149
class SmartServerRequestRevisionHistory(SmartServerBranchRequest):
 
150
 
 
151
    def do_with_branch(self, branch):
 
152
        """Get the revision history for the branch.
 
153
 
 
154
        The revision list is returned as the body content,
 
155
        with each revision utf8 encoded and \x00 joined.
 
156
        """
 
157
        return SuccessfulSmartServerResponse(
 
158
            ('ok', ), ('\x00'.join(branch.revision_history())))
 
159
 
 
160
 
 
161
class SmartServerBranchRequestLastRevisionInfo(SmartServerBranchRequest):
 
162
 
 
163
    def do_with_branch(self, branch):
 
164
        """Return branch.last_revision_info().
 
165
 
 
166
        The revno is encoded in decimal, the revision_id is encoded as utf8.
 
167
        """
 
168
        revno, last_revision = branch.last_revision_info()
 
169
        return SuccessfulSmartServerResponse(('ok', str(revno), last_revision))
 
170
 
 
171
 
 
172
class SmartServerSetTipRequest(SmartServerLockedBranchRequest):
 
173
    """Base class for handling common branch request logic for requests that
 
174
    update the branch tip.
 
175
    """
 
176
 
 
177
    def do_with_locked_branch(self, branch, *args):
 
178
        try:
 
179
            return self.do_tip_change_with_locked_branch(branch, *args)
 
180
        except errors.TipChangeRejected, e:
 
181
            msg = e.msg
 
182
            if isinstance(msg, unicode):
 
183
                msg = msg.encode('utf-8')
 
184
            return FailedSmartServerResponse(('TipChangeRejected', msg))
 
185
 
 
186
 
 
187
class SmartServerBranchRequestSetConfigOption(SmartServerLockedBranchRequest):
 
188
    """Set an option in the branch configuration."""
 
189
 
 
190
    def do_with_locked_branch(self, branch, value, name, section):
 
191
        if not section:
 
192
            section = None
 
193
        branch._get_config().set_option(value.decode('utf8'), name, section)
 
194
        return SuccessfulSmartServerResponse(())
 
195
 
 
196
 
 
197
class SmartServerBranchRequestSetLastRevision(SmartServerSetTipRequest):
 
198
 
 
199
    def do_tip_change_with_locked_branch(self, branch, new_last_revision_id):
 
200
        if new_last_revision_id == 'null:':
 
201
            branch.set_revision_history([])
 
202
        else:
 
203
            if not branch.repository.has_revision(new_last_revision_id):
 
204
                return FailedSmartServerResponse(
 
205
                    ('NoSuchRevision', new_last_revision_id))
 
206
            branch.set_revision_history(branch._lefthand_history(
 
207
                new_last_revision_id, None, None))
 
208
        return SuccessfulSmartServerResponse(('ok',))
 
209
 
 
210
 
 
211
class SmartServerBranchRequestSetLastRevisionEx(SmartServerSetTipRequest):
 
212
 
 
213
    def do_tip_change_with_locked_branch(self, branch, new_last_revision_id,
 
214
            allow_divergence, allow_overwrite_descendant):
 
215
        """Set the last revision of the branch.
 
216
 
 
217
        New in 1.6.
 
218
 
 
219
        :param new_last_revision_id: the revision ID to set as the last
 
220
            revision of the branch.
 
221
        :param allow_divergence: A flag.  If non-zero, change the revision ID
 
222
            even if the new_last_revision_id's ancestry has diverged from the
 
223
            current last revision.  If zero, a 'Diverged' error will be
 
224
            returned if new_last_revision_id is not a descendant of the current
 
225
            last revision.
 
226
        :param allow_overwrite_descendant:  A flag.  If zero and
 
227
            new_last_revision_id is not a descendant of the current last
 
228
            revision, then the last revision will not be changed.  If non-zero
 
229
            and there is no divergence, then the last revision is always
 
230
            changed.
 
231
 
 
232
        :returns: on success, a tuple of ('ok', revno, revision_id), where
 
233
            revno and revision_id are the new values of the current last
 
234
            revision info.  The revision_id might be different to the
 
235
            new_last_revision_id if allow_overwrite_descendant was not set.
 
236
        """
 
237
        do_not_overwrite_descendant = not allow_overwrite_descendant
 
238
        try:
 
239
            last_revno, last_rev = branch.last_revision_info()
 
240
            graph = branch.repository.get_graph()
 
241
            if not allow_divergence or do_not_overwrite_descendant:
 
242
                relation = branch._revision_relations(
 
243
                    last_rev, new_last_revision_id, graph)
 
244
                if relation == 'diverged' and not allow_divergence:
 
245
                    return FailedSmartServerResponse(('Diverged',))
 
246
                if relation == 'a_descends_from_b' and do_not_overwrite_descendant:
 
247
                    return SuccessfulSmartServerResponse(
 
248
                        ('ok', last_revno, last_rev))
 
249
            new_revno = graph.find_distance_to_null(
 
250
                new_last_revision_id, [(last_rev, last_revno)])
 
251
            branch.set_last_revision_info(new_revno, new_last_revision_id)
 
252
        except errors.GhostRevisionsHaveNoRevno:
 
253
            return FailedSmartServerResponse(
 
254
                ('NoSuchRevision', new_last_revision_id))
 
255
        return SuccessfulSmartServerResponse(
 
256
            ('ok', new_revno, new_last_revision_id))
 
257
 
 
258
 
 
259
class SmartServerBranchRequestSetLastRevisionInfo(SmartServerSetTipRequest):
 
260
    """Branch.set_last_revision_info.  Sets the revno and the revision ID of
 
261
    the specified branch.
 
262
 
 
263
    New in bzrlib 1.4.
 
264
    """
 
265
 
 
266
    def do_tip_change_with_locked_branch(self, branch, new_revno,
 
267
            new_last_revision_id):
 
268
        try:
 
269
            branch.set_last_revision_info(int(new_revno), new_last_revision_id)
 
270
        except errors.NoSuchRevision:
 
271
            return FailedSmartServerResponse(
 
272
                ('NoSuchRevision', new_last_revision_id))
 
273
        return SuccessfulSmartServerResponse(('ok',))
 
274
 
 
275
 
 
276
class SmartServerBranchRequestSetParentLocation(SmartServerLockedBranchRequest):
 
277
    """Set the parent location for a branch.
 
278
    
 
279
    Takes a location to set, which must be utf8 encoded.
 
280
    """
 
281
 
 
282
    def do_with_locked_branch(self, branch, location):
 
283
        branch._set_parent_location(location)
 
284
        return SuccessfulSmartServerResponse(())
 
285
 
 
286
 
 
287
class SmartServerBranchRequestLockWrite(SmartServerBranchRequest):
 
288
 
 
289
    def do_with_branch(self, branch, branch_token='', repo_token=''):
 
290
        if branch_token == '':
 
291
            branch_token = None
 
292
        if repo_token == '':
 
293
            repo_token = None
 
294
        try:
 
295
            repo_token = branch.repository.lock_write(token=repo_token)
 
296
            try:
 
297
                branch_token = branch.lock_write(token=branch_token)
 
298
            finally:
 
299
                # this leaves the repository with 1 lock
 
300
                branch.repository.unlock()
 
301
        except errors.LockContention:
 
302
            return FailedSmartServerResponse(('LockContention',))
 
303
        except errors.TokenMismatch:
 
304
            return FailedSmartServerResponse(('TokenMismatch',))
 
305
        except errors.UnlockableTransport:
 
306
            return FailedSmartServerResponse(('UnlockableTransport',))
 
307
        except errors.LockFailed, e:
 
308
            return FailedSmartServerResponse(('LockFailed', str(e.lock), str(e.why)))
 
309
        if repo_token is None:
 
310
            repo_token = ''
 
311
        else:
 
312
            branch.repository.leave_lock_in_place()
 
313
        branch.leave_lock_in_place()
 
314
        branch.unlock()
 
315
        return SuccessfulSmartServerResponse(('ok', branch_token, repo_token))
 
316
 
 
317
 
 
318
class SmartServerBranchRequestUnlock(SmartServerBranchRequest):
 
319
 
 
320
    def do_with_branch(self, branch, branch_token, repo_token):
 
321
        try:
 
322
            branch.repository.lock_write(token=repo_token)
 
323
            try:
 
324
                branch.lock_write(token=branch_token)
 
325
            finally:
 
326
                branch.repository.unlock()
 
327
        except errors.TokenMismatch:
 
328
            return FailedSmartServerResponse(('TokenMismatch',))
 
329
        if repo_token:
 
330
            branch.repository.dont_leave_lock_in_place()
 
331
        branch.dont_leave_lock_in_place()
 
332
        branch.unlock()
 
333
        return SuccessfulSmartServerResponse(('ok',))
 
334