/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: Andrew Bennetts
  • Date: 2009-07-27 02:11:25 UTC
  • mto: This revision was merged to the branch mainline in revision 4573.
  • Revision ID: andrew.bennetts@canonical.com-20090727021125-ohf358magl25s786
Update test.

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='/'):
 
109
        SmartServerLockedBranchRequest.__init__(
 
110
            self, backing_transport, root_client_path)
 
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
        if not self.locked:
 
130
            # We never acquired the branch successfully in the first place, so
 
131
            # there's nothing more to do.
 
132
            return
 
133
        try:
 
134
            return SmartServerLockedBranchRequest.do_end(self)
 
135
        finally:
 
136
            # Only try unlocking if we locked successfully in the first place
 
137
            self.branch.unlock()
 
138
 
 
139
 
 
140
class SmartServerBranchRequestGetStackedOnURL(SmartServerBranchRequest):
 
141
 
 
142
    def do_with_branch(self, branch):
 
143
        stacked_on_url = branch.get_stacked_on_url()
 
144
        return SuccessfulSmartServerResponse(('ok', stacked_on_url))
 
145
 
 
146
 
 
147
class SmartServerRequestRevisionHistory(SmartServerBranchRequest):
 
148
 
 
149
    def do_with_branch(self, branch):
 
150
        """Get the revision history for the branch.
 
151
 
 
152
        The revision list is returned as the body content,
 
153
        with each revision utf8 encoded and \x00 joined.
 
154
        """
 
155
        return SuccessfulSmartServerResponse(
 
156
            ('ok', ), ('\x00'.join(branch.revision_history())))
 
157
 
 
158
 
 
159
class SmartServerBranchRequestLastRevisionInfo(SmartServerBranchRequest):
 
160
 
 
161
    def do_with_branch(self, branch):
 
162
        """Return branch.last_revision_info().
 
163
 
 
164
        The revno is encoded in decimal, the revision_id is encoded as utf8.
 
165
        """
 
166
        revno, last_revision = branch.last_revision_info()
 
167
        return SuccessfulSmartServerResponse(('ok', str(revno), last_revision))
 
168
 
 
169
 
 
170
class SmartServerSetTipRequest(SmartServerLockedBranchRequest):
 
171
    """Base class for handling common branch request logic for requests that
 
172
    update the branch tip.
 
173
    """
 
174
 
 
175
    def do_with_locked_branch(self, branch, *args):
 
176
        try:
 
177
            return self.do_tip_change_with_locked_branch(branch, *args)
 
178
        except errors.TipChangeRejected, e:
 
179
            msg = e.msg
 
180
            if isinstance(msg, unicode):
 
181
                msg = msg.encode('utf-8')
 
182
            return FailedSmartServerResponse(('TipChangeRejected', msg))
 
183
 
 
184
 
 
185
class SmartServerBranchRequestSetConfigOption(SmartServerLockedBranchRequest):
 
186
    """Set an option in the branch configuration."""
 
187
 
 
188
    def do_with_locked_branch(self, branch, value, name, section):
 
189
        if not section:
 
190
            section = None
 
191
        branch._get_config().set_option(value.decode('utf8'), name, section)
 
192
        return SuccessfulSmartServerResponse(())
 
193
 
 
194
 
 
195
class SmartServerBranchRequestSetLastRevision(SmartServerSetTipRequest):
 
196
 
 
197
    def do_tip_change_with_locked_branch(self, branch, new_last_revision_id):
 
198
        if new_last_revision_id == 'null:':
 
199
            branch.set_revision_history([])
 
200
        else:
 
201
            if not branch.repository.has_revision(new_last_revision_id):
 
202
                return FailedSmartServerResponse(
 
203
                    ('NoSuchRevision', new_last_revision_id))
 
204
            branch.set_revision_history(branch._lefthand_history(
 
205
                new_last_revision_id, None, None))
 
206
        return SuccessfulSmartServerResponse(('ok',))
 
207
 
 
208
 
 
209
class SmartServerBranchRequestSetLastRevisionEx(SmartServerSetTipRequest):
 
210
 
 
211
    def do_tip_change_with_locked_branch(self, branch, new_last_revision_id,
 
212
            allow_divergence, allow_overwrite_descendant):
 
213
        """Set the last revision of the branch.
 
214
 
 
215
        New in 1.6.
 
216
 
 
217
        :param new_last_revision_id: the revision ID to set as the last
 
218
            revision of the branch.
 
219
        :param allow_divergence: A flag.  If non-zero, change the revision ID
 
220
            even if the new_last_revision_id's ancestry has diverged from the
 
221
            current last revision.  If zero, a 'Diverged' error will be
 
222
            returned if new_last_revision_id is not a descendant of the current
 
223
            last revision.
 
224
        :param allow_overwrite_descendant:  A flag.  If zero and
 
225
            new_last_revision_id is not a descendant of the current last
 
226
            revision, then the last revision will not be changed.  If non-zero
 
227
            and there is no divergence, then the last revision is always
 
228
            changed.
 
229
 
 
230
        :returns: on success, a tuple of ('ok', revno, revision_id), where
 
231
            revno and revision_id are the new values of the current last
 
232
            revision info.  The revision_id might be different to the
 
233
            new_last_revision_id if allow_overwrite_descendant was not set.
 
234
        """
 
235
        do_not_overwrite_descendant = not allow_overwrite_descendant
 
236
        try:
 
237
            last_revno, last_rev = branch.last_revision_info()
 
238
            graph = branch.repository.get_graph()
 
239
            if not allow_divergence or do_not_overwrite_descendant:
 
240
                relation = branch._revision_relations(
 
241
                    last_rev, new_last_revision_id, graph)
 
242
                if relation == 'diverged' and not allow_divergence:
 
243
                    return FailedSmartServerResponse(('Diverged',))
 
244
                if relation == 'a_descends_from_b' and do_not_overwrite_descendant:
 
245
                    return SuccessfulSmartServerResponse(
 
246
                        ('ok', last_revno, last_rev))
 
247
            new_revno = graph.find_distance_to_null(
 
248
                new_last_revision_id, [(last_rev, last_revno)])
 
249
            branch.set_last_revision_info(new_revno, new_last_revision_id)
 
250
        except errors.GhostRevisionsHaveNoRevno:
 
251
            return FailedSmartServerResponse(
 
252
                ('NoSuchRevision', new_last_revision_id))
 
253
        return SuccessfulSmartServerResponse(
 
254
            ('ok', new_revno, new_last_revision_id))
 
255
 
 
256
 
 
257
class SmartServerBranchRequestSetLastRevisionInfo(SmartServerSetTipRequest):
 
258
    """Branch.set_last_revision_info.  Sets the revno and the revision ID of
 
259
    the specified branch.
 
260
 
 
261
    New in bzrlib 1.4.
 
262
    """
 
263
 
 
264
    def do_tip_change_with_locked_branch(self, branch, new_revno,
 
265
            new_last_revision_id):
 
266
        try:
 
267
            branch.set_last_revision_info(int(new_revno), new_last_revision_id)
 
268
        except errors.NoSuchRevision:
 
269
            return FailedSmartServerResponse(
 
270
                ('NoSuchRevision', new_last_revision_id))
 
271
        return SuccessfulSmartServerResponse(('ok',))
 
272
 
 
273
 
 
274
class SmartServerBranchRequestSetParentLocation(SmartServerLockedBranchRequest):
 
275
    """Set the parent location for a branch.
 
276
    
 
277
    Takes a location to set, which must be utf8 encoded.
 
278
    """
 
279
 
 
280
    def do_with_locked_branch(self, branch, location):
 
281
        branch._set_parent_location(location)
 
282
        return SuccessfulSmartServerResponse(())
 
283
 
 
284
 
 
285
class SmartServerBranchRequestLockWrite(SmartServerBranchRequest):
 
286
 
 
287
    def do_with_branch(self, branch, branch_token='', repo_token=''):
 
288
        if branch_token == '':
 
289
            branch_token = None
 
290
        if repo_token == '':
 
291
            repo_token = None
 
292
        try:
 
293
            repo_token = branch.repository.lock_write(token=repo_token)
 
294
            try:
 
295
                branch_token = branch.lock_write(token=branch_token)
 
296
            finally:
 
297
                # this leaves the repository with 1 lock
 
298
                branch.repository.unlock()
 
299
        except errors.LockContention:
 
300
            return FailedSmartServerResponse(('LockContention',))
 
301
        except errors.TokenMismatch:
 
302
            return FailedSmartServerResponse(('TokenMismatch',))
 
303
        except errors.UnlockableTransport:
 
304
            return FailedSmartServerResponse(('UnlockableTransport',))
 
305
        except errors.LockFailed, e:
 
306
            return FailedSmartServerResponse(('LockFailed', str(e.lock), str(e.why)))
 
307
        if repo_token is None:
 
308
            repo_token = ''
 
309
        else:
 
310
            branch.repository.leave_lock_in_place()
 
311
        branch.leave_lock_in_place()
 
312
        branch.unlock()
 
313
        return SuccessfulSmartServerResponse(('ok', branch_token, repo_token))
 
314
 
 
315
 
 
316
class SmartServerBranchRequestUnlock(SmartServerBranchRequest):
 
317
 
 
318
    def do_with_branch(self, branch, branch_token, repo_token):
 
319
        try:
 
320
            branch.repository.lock_write(token=repo_token)
 
321
            try:
 
322
                branch.lock_write(token=branch_token)
 
323
            finally:
 
324
                branch.repository.unlock()
 
325
        except errors.TokenMismatch:
 
326
            return FailedSmartServerResponse(('TokenMismatch',))
 
327
        if repo_token:
 
328
            branch.repository.dont_leave_lock_in_place()
 
329
        branch.dont_leave_lock_in_place()
 
330
        branch.unlock()
 
331
        return SuccessfulSmartServerResponse(('ok',))
 
332