~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/branch.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2009-05-06 04:07:37 UTC
  • mfrom: (4332.1.1 integration)
  • Revision ID: pqm@pqm.ubuntu.com-20090506040737-tdiqyojy2uia33qb
Make it easier to blackbox test rules (Marius Kruger)

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