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(
158
('ok', ), ('\x00'.join(branch.revision_history())))
84
return SmartServerResponse(('ok', ),
85
('\x00'.join(branch.revision_history())).encode('utf8'))
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(
99
('ok', str(revno), last_revision.encode('utf8')))
102
class SmartServerBranchRequestSetLastRevision(SmartServerLockedBranchRequest):
104
def do_with_locked_branch(self, branch, new_last_revision_id):
105
unicode_new_last_revision_id = new_last_revision_id.decode('utf-8') # XXX test
106
if new_last_revision_id == '':
201
107
branch.set_revision_history([])
203
if not branch.repository.has_revision(new_last_revision_id):
204
return FailedSmartServerResponse(
109
if not branch.repository.has_revision(unicode_new_last_revision_id):
110
return SmartServerResponse(
205
111
('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(())
112
branch.generate_revision_history(unicode_new_last_revision_id)
113
return SmartServerResponse(('ok',))
287
116
class SmartServerBranchRequestLockWrite(SmartServerBranchRequest):
289
118
def do_with_branch(self, branch, branch_token='', repo_token=''):
290
119
if branch_token == '':
291
120
branch_token = None
292
121
if repo_token == '':
293
122
repo_token = None
123
tokens = (branch_token, repo_token)
124
if 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()
127
branch_token, repo_token = branch.lock_write(tokens=tokens)
301
128
except errors.LockContention:
302
return FailedSmartServerResponse(('LockContention',))
129
return SmartServerResponse(('LockContention',))
303
130
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:
312
branch.repository.leave_lock_in_place()
131
return SmartServerResponse(('TokenMismatch',))
132
branch.repository.leave_lock_in_place()
313
133
branch.leave_lock_in_place()
315
return SuccessfulSmartServerResponse(('ok', branch_token, repo_token))
135
return SmartServerResponse(('ok', branch_token, repo_token))
318
138
class SmartServerBranchRequestUnlock(SmartServerBranchRequest):
320
140
def do_with_branch(self, branch, branch_token, repo_token):
141
tokens = branch_token, repo_token
322
branch.repository.lock_write(token=repo_token)
324
branch.lock_write(token=branch_token)
326
branch.repository.unlock()
143
tokens = branch.lock_write(tokens=tokens)
327
144
except errors.TokenMismatch:
328
return FailedSmartServerResponse(('TokenMismatch',))
330
branch.repository.dont_leave_lock_in_place()
145
return SmartServerResponse(('TokenMismatch',))
146
branch.repository.dont_leave_lock_in_place()
331
147
branch.dont_leave_lock_in_place()
333
return SuccessfulSmartServerResponse(('ok',))
149
return SmartServerResponse(('ok',))