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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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.revision import NULL_REVISION
23
from bzrlib.smart.request import SmartServerRequest, SmartServerResponse
22
from bzrlib.smart.request import (
23
FailedSmartServerResponse,
25
SuccessfulSmartServerResponse,
26
29
class SmartServerBranchRequest(SmartServerRequest):
27
"""Base class for handling common branch request logic."""
30
"""Base class for handling common branch request logic.
29
33
def do(self, path, *args):
30
34
"""Execute a request for a branch at path.
36
All Branch requests take a path to the branch as their first argument.
32
38
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().
34
transport = self._backing_transport.clone(path)
44
transport = self.transport_from_client_path(path)
35
45
bzrdir = BzrDir.open_from_transport(transport)
36
46
if bzrdir.get_branch_reference() is not None:
37
47
raise errors.NotBranchError(transport.base)
38
branch = bzrdir.open_branch()
48
branch = bzrdir.open_branch(ignore_fallbacks=True)
39
49
return self.do_with_branch(branch, *args)
52
62
processed. The physical lock state won't be changed.
54
64
# XXX: write a test for LockContention
55
branch.lock_write(tokens=(branch_token, repo_token))
65
branch.repository.lock_write(token=repo_token)
57
return self.do_with_locked_branch(branch, *args)
67
branch.lock_write(token=branch_token)
69
return self.do_with_locked_branch(branch, *args)
73
branch.repository.unlock()
62
76
class SmartServerBranchGetConfigFile(SmartServerBranchRequest):
64
78
def do_with_branch(self, branch):
65
"""Return the content of branch.control_files.get('branch.conf').
79
"""Return the content of branch.conf
67
81
The body is not utf8 decoded - its the literal bytestream from disk.
70
content = branch.control_files.get('branch.conf').read()
84
content = branch._transport.get_bytes('branch.conf')
71
85
except errors.NoSuchFile:
73
return SmartServerResponse( ('ok', ), content)
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))
76
149
class SmartServerRequestRevisionHistory(SmartServerBranchRequest):
81
154
The revision list is returned as the body content,
82
155
with each revision utf8 encoded and \x00 joined.
84
return SmartServerResponse(
157
return SuccessfulSmartServerResponse(
85
158
('ok', ), ('\x00'.join(branch.revision_history())))
88
161
class SmartServerBranchRequestLastRevisionInfo(SmartServerBranchRequest):
90
163
def do_with_branch(self, branch):
91
164
"""Return branch.last_revision_info().
93
166
The revno is encoded in decimal, the revision_id is encoded as utf8.
95
168
revno, last_revision = branch.last_revision_info()
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 == '':
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:':
105
201
branch.set_revision_history([])
107
203
if not branch.repository.has_revision(new_last_revision_id):
108
return SmartServerResponse(
204
return FailedSmartServerResponse(
109
205
('NoSuchRevision', new_last_revision_id))
110
branch.generate_revision_history(new_last_revision_id)
111
return SmartServerResponse(('ok',))
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(())
114
287
class SmartServerBranchRequestLockWrite(SmartServerBranchRequest):
116
289
def do_with_branch(self, branch, branch_token='', repo_token=''):
117
290
if branch_token == '':
118
291
branch_token = None
119
292
if repo_token == '':
120
293
repo_token = None
121
tokens = (branch_token, repo_token)
122
if tokens == ('', ''):
125
branch_token, repo_token = branch.lock_write(tokens=tokens)
295
repo_token = branch.repository.lock_write(token=repo_token)
297
branch_token = branch.lock_write(token=branch_token)
299
# this leaves the repository with 1 lock
300
branch.repository.unlock()
126
301
except errors.LockContention:
127
return SmartServerResponse(('LockContention',))
302
return FailedSmartServerResponse(('LockContention',))
128
303
except errors.TokenMismatch:
129
return SmartServerResponse(('TokenMismatch',))
304
return FailedSmartServerResponse(('TokenMismatch',))
130
305
except errors.UnlockableTransport:
131
return SmartServerResponse(('UnlockableTransport',))
132
branch.repository.leave_lock_in_place()
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:
312
branch.repository.leave_lock_in_place()
133
313
branch.leave_lock_in_place()
135
return SmartServerResponse(('ok', branch_token, repo_token))
315
return SuccessfulSmartServerResponse(('ok', branch_token, repo_token))
138
318
class SmartServerBranchRequestUnlock(SmartServerBranchRequest):
140
320
def do_with_branch(self, branch, branch_token, repo_token):
141
tokens = branch_token, repo_token
143
tokens = branch.lock_write(tokens=tokens)
322
branch.repository.lock_write(token=repo_token)
324
branch.lock_write(token=branch_token)
326
branch.repository.unlock()
144
327
except errors.TokenMismatch:
145
return SmartServerResponse(('TokenMismatch',))
146
branch.repository.dont_leave_lock_in_place()
328
return FailedSmartServerResponse(('TokenMismatch',))
330
branch.repository.dont_leave_lock_in_place()
147
331
branch.dont_leave_lock_in_place()
149
return SmartServerResponse(('ok',))
333
return SuccessfulSmartServerResponse(('ok',))