13
13
# You should have received a copy of the GNU General Public License
14
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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
17
"""Server-side branch related request implmentations."""
20
20
from bzrlib import errors
21
21
from bzrlib.bzrdir import BzrDir
22
from bzrlib.smart.request import (
23
FailedSmartServerResponse,
25
SuccessfulSmartServerResponse,
22
from bzrlib.revision import NULL_REVISION
23
from bzrlib.smart.request import SmartServerRequest, SmartServerResponse
29
26
class SmartServerBranchRequest(SmartServerRequest):
30
"""Base class for handling common branch request logic.
27
"""Base class for handling common branch request logic."""
33
29
def do(self, path, *args):
34
30
"""Execute a request for a branch at path.
36
All Branch requests take a path to the branch as their first argument.
38
32
If the branch is a branch reference, NotBranchError is raised.
40
:param path: The path for the repository as received from the
42
:return: A SmartServerResponse from self.do_with_branch().
44
transport = self.transport_from_client_path(path)
34
transport = self._backing_transport.clone(path)
45
35
bzrdir = BzrDir.open_from_transport(transport)
46
36
if bzrdir.get_branch_reference() is not None:
47
37
raise errors.NotBranchError(transport.base)
48
branch = bzrdir.open_branch(ignore_fallbacks=True)
38
branch = bzrdir.open_branch()
49
39
return self.do_with_branch(branch, *args)
62
52
processed. The physical lock state won't be changed.
64
54
# XXX: write a test for LockContention
65
branch.repository.lock_write(token=repo_token)
55
branch.lock_write(tokens=(branch_token, repo_token))
67
branch.lock_write(token=branch_token)
69
return self.do_with_locked_branch(branch, *args)
57
return self.do_with_locked_branch(branch, *args)
73
branch.repository.unlock()
76
62
class SmartServerBranchGetConfigFile(SmartServerBranchRequest):
78
64
def do_with_branch(self, branch):
79
"""Return the content of branch.conf
65
"""Return the content of branch.control_files.get('branch.conf').
81
67
The body is not utf8 decoded - its the literal bytestream from disk.
84
content = branch._transport.get_bytes('branch.conf')
70
content = branch.control_files.get('branch.conf').read()
85
71
except errors.NoSuchFile:
87
return SuccessfulSmartServerResponse( ('ok', ), content)
90
class SmartServerBranchGetParent(SmartServerBranchRequest):
92
def do_with_branch(self, branch):
93
"""Return the parent of branch."""
94
parent = branch._get_parent_location() or ''
95
return SuccessfulSmartServerResponse((parent,))
98
class SmartServerBranchGetTagsBytes(SmartServerBranchRequest):
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,))
106
class SmartServerBranchSetTagsBytes(SmartServerLockedBranchRequest):
108
def __init__(self, backing_transport, root_client_path='/', jail_root=None):
109
SmartServerLockedBranchRequest.__init__(
110
self, backing_transport, root_client_path, jail_root)
113
def do_with_locked_branch(self, branch):
114
"""Call _set_tags_bytes for a branch.
118
# We need to keep this branch locked until we get a body with the tags
121
self.branch.lock_write()
124
def do_body(self, bytes):
125
self.branch._set_tags_bytes(bytes)
126
return SuccessfulSmartServerResponse(())
129
# TODO: this request shouldn't have to do this housekeeping manually.
130
# Some of this logic probably belongs in a base class.
132
# We never acquired the branch successfully in the first place, so
133
# there's nothing more to do.
136
return SmartServerLockedBranchRequest.do_end(self)
138
# Only try unlocking if we locked successfully in the first place
142
class SmartServerBranchRequestGetStackedOnURL(SmartServerBranchRequest):
144
def do_with_branch(self, branch):
145
stacked_on_url = branch.get_stacked_on_url()
146
return SuccessfulSmartServerResponse(('ok', stacked_on_url))
73
return SmartServerResponse( ('ok', ), content)
149
76
class SmartServerRequestRevisionHistory(SmartServerBranchRequest):
154
81
The revision list is returned as the body content,
155
82
with each revision utf8 encoded and \x00 joined.
157
return SuccessfulSmartServerResponse(
84
return SmartServerResponse(
158
85
('ok', ), ('\x00'.join(branch.revision_history())))
161
88
class SmartServerBranchRequestLastRevisionInfo(SmartServerBranchRequest):
163
90
def do_with_branch(self, branch):
164
91
"""Return branch.last_revision_info().
166
93
The revno is encoded in decimal, the revision_id is encoded as utf8.
168
95
revno, last_revision = branch.last_revision_info()
169
return SuccessfulSmartServerResponse(('ok', str(revno), last_revision))
172
class SmartServerSetTipRequest(SmartServerLockedBranchRequest):
173
"""Base class for handling common branch request logic for requests that
174
update the branch tip.
177
def do_with_locked_branch(self, branch, *args):
179
return self.do_tip_change_with_locked_branch(branch, *args)
180
except errors.TipChangeRejected, e:
182
if isinstance(msg, unicode):
183
msg = msg.encode('utf-8')
184
return FailedSmartServerResponse(('TipChangeRejected', msg))
187
class SmartServerBranchRequestSetConfigOption(SmartServerLockedBranchRequest):
188
"""Set an option in the branch configuration."""
190
def do_with_locked_branch(self, branch, value, name, section):
193
branch._get_config().set_option(value.decode('utf8'), name, section)
194
return SuccessfulSmartServerResponse(())
197
class SmartServerBranchRequestSetLastRevision(SmartServerSetTipRequest):
199
def do_tip_change_with_locked_branch(self, branch, new_last_revision_id):
200
if new_last_revision_id == 'null:':
96
if last_revision == NULL_REVISION:
98
return SmartServerResponse(('ok', str(revno), last_revision))
101
class SmartServerBranchRequestSetLastRevision(SmartServerLockedBranchRequest):
103
def do_with_locked_branch(self, branch, new_last_revision_id):
104
if new_last_revision_id == '':
201
105
branch.set_revision_history([])
203
107
if not branch.repository.has_revision(new_last_revision_id):
204
return FailedSmartServerResponse(
108
return SmartServerResponse(
205
109
('NoSuchRevision', new_last_revision_id))
206
branch.set_revision_history(branch._lefthand_history(
207
new_last_revision_id, None, None))
208
return SuccessfulSmartServerResponse(('ok',))
211
class SmartServerBranchRequestSetLastRevisionEx(SmartServerSetTipRequest):
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.
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
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
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.
237
do_not_overwrite_descendant = not allow_overwrite_descendant
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))
259
class SmartServerBranchRequestSetLastRevisionInfo(SmartServerSetTipRequest):
260
"""Branch.set_last_revision_info. Sets the revno and the revision ID of
261
the specified branch.
266
def do_tip_change_with_locked_branch(self, branch, new_revno,
267
new_last_revision_id):
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',))
276
class SmartServerBranchRequestSetParentLocation(SmartServerLockedBranchRequest):
277
"""Set the parent location for a branch.
279
Takes a location to set, which must be utf8 encoded.
282
def do_with_locked_branch(self, branch, location):
283
branch._set_parent_location(location)
284
return SuccessfulSmartServerResponse(())
110
branch.generate_revision_history(new_last_revision_id)
111
return SmartServerResponse(('ok',))
287
114
class SmartServerBranchRequestLockWrite(SmartServerBranchRequest):
289
116
def do_with_branch(self, branch, branch_token='', repo_token=''):
290
117
if branch_token == '':
291
118
branch_token = None
292
119
if repo_token == '':
293
120
repo_token = None
121
tokens = (branch_token, repo_token)
122
if tokens == ('', ''):
295
repo_token = branch.repository.lock_write(
296
token=repo_token).repository_token
298
branch_token = branch.lock_write(
299
token=branch_token).branch_token
301
# this leaves the repository with 1 lock
302
branch.repository.unlock()
125
branch_token, repo_token = branch.lock_write(tokens=tokens)
303
126
except errors.LockContention:
304
return FailedSmartServerResponse(('LockContention',))
127
return SmartServerResponse(('LockContention',))
305
128
except errors.TokenMismatch:
306
return FailedSmartServerResponse(('TokenMismatch',))
129
return SmartServerResponse(('TokenMismatch',))
307
130
except errors.UnlockableTransport:
308
return FailedSmartServerResponse(('UnlockableTransport',))
309
except errors.LockFailed, e:
310
return FailedSmartServerResponse(('LockFailed', str(e.lock), str(e.why)))
311
if repo_token is None:
314
branch.repository.leave_lock_in_place()
131
return SmartServerResponse(('UnlockableTransport',))
132
branch.repository.leave_lock_in_place()
315
133
branch.leave_lock_in_place()
317
return SuccessfulSmartServerResponse(('ok', branch_token, repo_token))
135
return SmartServerResponse(('ok', branch_token, repo_token))
320
138
class SmartServerBranchRequestUnlock(SmartServerBranchRequest):
322
140
def do_with_branch(self, branch, branch_token, repo_token):
141
tokens = branch_token, repo_token
324
branch.repository.lock_write(token=repo_token)
326
branch.lock_write(token=branch_token)
328
branch.repository.unlock()
143
tokens = branch.lock_write(tokens=tokens)
329
144
except errors.TokenMismatch:
330
return FailedSmartServerResponse(('TokenMismatch',))
332
branch.repository.dont_leave_lock_in_place()
145
return SmartServerResponse(('TokenMismatch',))
146
branch.repository.dont_leave_lock_in_place()
333
147
branch.dont_leave_lock_in_place()
335
return SuccessfulSmartServerResponse(('ok',))
149
return SmartServerResponse(('ok',))