Merge lp:~spiv/bzr/bug-440952-bzrdir into lp:bzr
- bug-440952-bzrdir
- Merge into bzr.dev
Proposed by
Andrew Bennetts
Status: | Merged |
---|---|
Approved by: | John A Meinel |
Approved revision: | not available |
Merged at revision: | not available |
Proposed branch: | lp:~spiv/bzr/bug-440952-bzrdir |
Merge into: | lp:bzr |
Diff against target: |
511 lines (+253/-34) 11 files modified
NEWS (+8/-0) bzrlib/branch.py (+2/-2) bzrlib/errors.py (+24/-3) bzrlib/remote.py (+30/-14) bzrlib/smart/bzrdir.py (+40/-6) bzrlib/smart/request.py (+3/-0) bzrlib/tests/blackbox/test_shared_repository.py (+19/-1) bzrlib/tests/per_branch/test_push.py (+1/-1) bzrlib/tests/test_errors.py (+32/-0) bzrlib/tests/test_remote.py (+16/-7) bzrlib/tests/test_smart.py (+78/-0) |
To merge this branch: | bzr merge lp:~spiv/bzr/bug-440952-bzrdir |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
John A Meinel | Approve | ||
Review via email: mp+17183@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Andrew Bennetts (spiv) wrote : | # |
Revision history for this message
John A Meinel (jameinel) wrote : | # |
The final output seems to be:
$ awbzr branch ../bzr
bzr: ERROR: Not a branch: "C:/Users/
I'm not 100% happy with that, but it is better than nothing. The code looks good.
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'NEWS' |
2 | --- NEWS 2010-01-15 04:06:45 +0000 |
3 | +++ NEWS 2010-01-15 05:14:10 +0000 |
4 | @@ -116,6 +116,10 @@ |
5 | ``try``/``finally`` blocks where applicable as it is simpler and more |
6 | robust. (Andrew Bennetts) |
7 | |
8 | +* Attempts to open a shared repository as a branch (e.g. ``bzr branch |
9 | + path/to/repo``) will now include "location is a repository" as a hint in |
10 | + the error message. (Brian de Alwis, Andrew Bennetts, #440952) |
11 | + |
12 | * Push will now inform the user when they are trying to push to a foreign |
13 | VCS for which roundtripping is not supported, and will suggest them to |
14 | use dpush. (Jelmer Vernooij) |
15 | @@ -166,6 +170,10 @@ |
16 | Internals |
17 | ********* |
18 | |
19 | +* Added ``BzrDir.open_branchV3`` smart server request, which can receive |
20 | + a string of details (such as "location is a repository") as part of a |
21 | + ``nobranch`` response. (Andrew Bennetts, #440952) |
22 | + |
23 | * New helper osutils.UnicodeOrBytesToBytesWriter which encodes unicode |
24 | objects but passes str objects straight through. This is used for |
25 | selftest but may be useful for diff and other operations that generate |
26 | |
27 | === modified file 'bzrlib/branch.py' |
28 | --- bzrlib/branch.py 2010-01-08 05:28:17 +0000 |
29 | +++ bzrlib/branch.py 2010-01-15 05:14:10 +0000 |
30 | @@ -1441,7 +1441,7 @@ |
31 | format_string = transport.get_bytes("format") |
32 | return klass._formats[format_string] |
33 | except errors.NoSuchFile: |
34 | - raise errors.NotBranchError(path=transport.base) |
35 | + raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir) |
36 | except KeyError: |
37 | raise errors.UnknownFormatError(format=format_string, kind='branch') |
38 | |
39 | @@ -1796,7 +1796,7 @@ |
40 | _repository=a_bzrdir.find_repository(), |
41 | ignore_fallbacks=ignore_fallbacks) |
42 | except errors.NoSuchFile: |
43 | - raise errors.NotBranchError(path=transport.base) |
44 | + raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir) |
45 | |
46 | def __init__(self): |
47 | super(BranchFormatMetadir, self).__init__() |
48 | |
49 | === modified file 'bzrlib/errors.py' |
50 | --- bzrlib/errors.py 2010-01-08 06:33:05 +0000 |
51 | +++ bzrlib/errors.py 2010-01-15 05:14:10 +0000 |
52 | @@ -702,11 +702,32 @@ |
53 | # TODO: Probably this behavior of should be a common superclass |
54 | class NotBranchError(PathError): |
55 | |
56 | - _fmt = 'Not a branch: "%(path)s".' |
57 | + _fmt = 'Not a branch: "%(path)s"%(detail)s.' |
58 | |
59 | - def __init__(self, path): |
60 | + def __init__(self, path, detail=None, bzrdir=None): |
61 | import bzrlib.urlutils as urlutils |
62 | - self.path = urlutils.unescape_for_display(path, 'ascii') |
63 | + path = urlutils.unescape_for_display(path, 'ascii') |
64 | + if detail is not None: |
65 | + detail = ': ' + detail |
66 | + self.detail = detail |
67 | + self.bzrdir = bzrdir |
68 | + PathError.__init__(self, path=path) |
69 | + |
70 | + def _format(self): |
71 | + # XXX: Ideally self.detail would be a property, but Exceptions in |
72 | + # Python 2.4 have to be old-style classes so properties don't work. |
73 | + # Instead we override _format. |
74 | + if self.detail is None: |
75 | + if self.bzrdir is not None: |
76 | + try: |
77 | + self.bzrdir.open_repository() |
78 | + except NoRepositoryPresent: |
79 | + self.detail = '' |
80 | + else: |
81 | + self.detail = ': location is a repository' |
82 | + else: |
83 | + self.detail = '' |
84 | + return PathError._format(self) |
85 | |
86 | |
87 | class NoSubmitBranch(PathError): |
88 | |
89 | === modified file 'bzrlib/remote.py' |
90 | --- bzrlib/remote.py 2009-12-15 20:32:34 +0000 |
91 | +++ bzrlib/remote.py 2010-01-15 05:14:10 +0000 |
92 | @@ -284,21 +284,32 @@ |
93 | def _get_branch_reference(self): |
94 | path = self._path_for_remote_call(self._client) |
95 | medium = self._client._medium |
96 | - if not medium._is_remote_before((1, 13)): |
97 | + candidate_calls = [ |
98 | + ('BzrDir.open_branchV3', (2, 1)), |
99 | + ('BzrDir.open_branchV2', (1, 13)), |
100 | + ('BzrDir.open_branch', None), |
101 | + ] |
102 | + for verb, required_version in candidate_calls: |
103 | + if required_version and medium._is_remote_before(required_version): |
104 | + continue |
105 | try: |
106 | - response = self._call('BzrDir.open_branchV2', path) |
107 | - if response[0] not in ('ref', 'branch'): |
108 | - raise errors.UnexpectedSmartServerResponse(response) |
109 | - return response |
110 | + response = self._call(verb, path) |
111 | except errors.UnknownSmartMethod: |
112 | - medium._remember_remote_is_before((1, 13)) |
113 | - response = self._call('BzrDir.open_branch', path) |
114 | - if response[0] != 'ok': |
115 | + if required_version is None: |
116 | + raise |
117 | + medium._remember_remote_is_before(required_version) |
118 | + else: |
119 | + break |
120 | + if verb == 'BzrDir.open_branch': |
121 | + if response[0] != 'ok': |
122 | + raise errors.UnexpectedSmartServerResponse(response) |
123 | + if response[1] != '': |
124 | + return ('ref', response[1]) |
125 | + else: |
126 | + return ('branch', '') |
127 | + if response[0] not in ('ref', 'branch'): |
128 | raise errors.UnexpectedSmartServerResponse(response) |
129 | - if response[1] != '': |
130 | - return ('ref', response[1]) |
131 | - else: |
132 | - return ('branch', '') |
133 | + return response |
134 | |
135 | def _get_tree_branch(self): |
136 | """See BzrDir._get_tree_branch().""" |
137 | @@ -2823,8 +2834,13 @@ |
138 | raise NoSuchRevision(find('branch'), err.error_args[0]) |
139 | elif err.error_verb == 'nosuchrevision': |
140 | raise NoSuchRevision(find('repository'), err.error_args[0]) |
141 | - elif err.error_tuple == ('nobranch',): |
142 | - raise errors.NotBranchError(path=find('bzrdir').root_transport.base) |
143 | + elif err.error_verb == 'nobranch': |
144 | + if len(err.error_args) >= 1: |
145 | + extra = err.error_args[0] |
146 | + else: |
147 | + extra = None |
148 | + raise errors.NotBranchError(path=find('bzrdir').root_transport.base, |
149 | + detail=extra) |
150 | elif err.error_verb == 'norepository': |
151 | raise errors.NoRepositoryPresent(find('bzrdir')) |
152 | elif err.error_verb == 'LockContention': |
153 | |
154 | === modified file 'bzrlib/smart/bzrdir.py' |
155 | --- bzrlib/smart/bzrdir.py 2009-09-22 00:34:10 +0000 |
156 | +++ bzrlib/smart/bzrdir.py 2010-01-15 05:14:10 +0000 |
157 | @@ -88,8 +88,8 @@ |
158 | try: |
159 | self._bzrdir = BzrDir.open_from_transport( |
160 | self.transport_from_client_path(path)) |
161 | - except errors.NotBranchError: |
162 | - return FailedSmartServerResponse(('nobranch', )) |
163 | + except errors.NotBranchError, e: |
164 | + return FailedSmartServerResponse(('nobranch',)) |
165 | return self.do_bzrdir_request(*args) |
166 | |
167 | def _boolean_to_yes_no(self, a_boolean): |
168 | @@ -465,8 +465,8 @@ |
169 | return SuccessfulSmartServerResponse(('ok', '')) |
170 | else: |
171 | return SuccessfulSmartServerResponse(('ok', reference_url)) |
172 | - except errors.NotBranchError: |
173 | - return FailedSmartServerResponse(('nobranch', )) |
174 | + except errors.NotBranchError, e: |
175 | + return FailedSmartServerResponse(('nobranch',)) |
176 | |
177 | |
178 | class SmartServerRequestOpenBranchV2(SmartServerRequestBzrDir): |
179 | @@ -481,5 +481,39 @@ |
180 | return SuccessfulSmartServerResponse(('branch', format)) |
181 | else: |
182 | return SuccessfulSmartServerResponse(('ref', reference_url)) |
183 | - except errors.NotBranchError: |
184 | - return FailedSmartServerResponse(('nobranch', )) |
185 | + except errors.NotBranchError, e: |
186 | + return FailedSmartServerResponse(('nobranch',)) |
187 | + |
188 | + |
189 | +class SmartServerRequestOpenBranchV3(SmartServerRequestBzrDir): |
190 | + |
191 | + def do_bzrdir_request(self): |
192 | + """Open a branch at path and return the reference or format. |
193 | + |
194 | + This version introduced in 2.1. |
195 | + |
196 | + Differences to SmartServerRequestOpenBranchV2: |
197 | + * can return 2-element ('nobranch', extra), where 'extra' is a string |
198 | + with an explanation like 'location is a repository'. Previously |
199 | + a 'nobranch' response would never have more than one element. |
200 | + """ |
201 | + try: |
202 | + reference_url = self._bzrdir.get_branch_reference() |
203 | + if reference_url is None: |
204 | + br = self._bzrdir.open_branch(ignore_fallbacks=True) |
205 | + format = br._format.network_name() |
206 | + return SuccessfulSmartServerResponse(('branch', format)) |
207 | + else: |
208 | + return SuccessfulSmartServerResponse(('ref', reference_url)) |
209 | + except errors.NotBranchError, e: |
210 | + # Stringify the exception so that its .detail attribute will be |
211 | + # filled out. |
212 | + str(e) |
213 | + resp = ('nobranch',) |
214 | + detail = e.detail |
215 | + if detail: |
216 | + if detail.startswith(': '): |
217 | + detail = detail[2:] |
218 | + resp += (detail,) |
219 | + return FailedSmartServerResponse(resp) |
220 | + |
221 | |
222 | === modified file 'bzrlib/smart/request.py' |
223 | --- bzrlib/smart/request.py 2009-12-21 17:00:29 +0000 |
224 | +++ bzrlib/smart/request.py 2010-01-15 05:14:10 +0000 |
225 | @@ -561,6 +561,9 @@ |
226 | 'BzrDir.open_branchV2', 'bzrlib.smart.bzrdir', |
227 | 'SmartServerRequestOpenBranchV2') |
228 | request_handlers.register_lazy( |
229 | + 'BzrDir.open_branchV3', 'bzrlib.smart.bzrdir', |
230 | + 'SmartServerRequestOpenBranchV3') |
231 | +request_handlers.register_lazy( |
232 | 'delete', 'bzrlib.smart.vfs', 'DeleteRequest') |
233 | request_handlers.register_lazy( |
234 | 'get', 'bzrlib.smart.vfs', 'GetRequest') |
235 | |
236 | === modified file 'bzrlib/tests/blackbox/test_shared_repository.py' |
237 | --- bzrlib/tests/blackbox/test_shared_repository.py 2009-09-23 23:58:10 +0000 |
238 | +++ bzrlib/tests/blackbox/test_shared_repository.py 2010-01-15 05:14:10 +0000 |
239 | @@ -18,7 +18,7 @@ |
240 | |
241 | import os |
242 | |
243 | -from bzrlib.bzrdir import BzrDir |
244 | +from bzrlib.bzrdir import BzrDir, BzrDirMetaFormat1 |
245 | import bzrlib.errors as errors |
246 | from bzrlib.tests import TestCaseInTempDir |
247 | |
248 | @@ -120,3 +120,21 @@ |
249 | # become necessary for this use case. Please do not adjust this number |
250 | # upwards without agreement from bzr's network support maintainers. |
251 | self.assertLength(15, self.hpss_calls) |
252 | + |
253 | + def test_notification_on_branch_from_repository(self): |
254 | + out, err = self.run_bzr("init-repository -q a") |
255 | + self.assertEqual(out, "") |
256 | + self.assertEqual(err, "") |
257 | + dir = BzrDir.open('a') |
258 | + dir.open_repository() # there is a repository there |
259 | + e = self.assertRaises(errors.NotBranchError, dir.open_branch) |
260 | + self.assertContainsRe(str(e), "location is a repository") |
261 | + |
262 | + def test_notification_on_branch_from_nonrepository(self): |
263 | + fmt = BzrDirMetaFormat1() |
264 | + t = self.get_transport() |
265 | + t.mkdir('a') |
266 | + dir = fmt.initialize_on_transport(t.clone('a')) |
267 | + self.assertRaises(errors.NoRepositoryPresent, dir.open_repository) |
268 | + e = self.assertRaises(errors.NotBranchError, dir.open_branch) |
269 | + self.assertNotContainsRe(str(e), "location is a repository") |
270 | |
271 | === modified file 'bzrlib/tests/per_branch/test_push.py' |
272 | --- bzrlib/tests/per_branch/test_push.py 2009-09-24 05:31:23 +0000 |
273 | +++ bzrlib/tests/per_branch/test_push.py 2010-01-15 05:14:10 +0000 |
274 | @@ -414,7 +414,7 @@ |
275 | self.empty_branch.push(target) |
276 | self.assertEqual( |
277 | ['BzrDir.open_2.1', |
278 | - 'BzrDir.open_branchV2', |
279 | + 'BzrDir.open_branchV3', |
280 | 'BzrDir.find_repositoryV3', |
281 | 'Branch.get_stacked_on_url', |
282 | 'Branch.lock_write', |
283 | |
284 | === modified file 'bzrlib/tests/test_errors.py' |
285 | --- bzrlib/tests/test_errors.py 2010-01-08 06:33:05 +0000 |
286 | +++ bzrlib/tests/test_errors.py 2010-01-15 05:14:10 +0000 |
287 | @@ -623,6 +623,38 @@ |
288 | self.assertEqual( |
289 | 'Repository dummy repo cannot suspend a write group.', str(err)) |
290 | |
291 | + def test_not_branch_no_args(self): |
292 | + err = errors.NotBranchError('path') |
293 | + self.assertEqual('Not a branch: "path".', str(err)) |
294 | + |
295 | + def test_not_branch_bzrdir_with_repo(self): |
296 | + bzrdir = self.make_repository('repo').bzrdir |
297 | + err = errors.NotBranchError('path', bzrdir=bzrdir) |
298 | + self.assertEqual( |
299 | + 'Not a branch: "path": location is a repository.', str(err)) |
300 | + |
301 | + def test_not_branch_bzrdir_without_repo(self): |
302 | + bzrdir = self.make_bzrdir('bzrdir') |
303 | + err = errors.NotBranchError('path', bzrdir=bzrdir) |
304 | + self.assertEqual('Not a branch: "path".', str(err)) |
305 | + |
306 | + def test_not_branch_laziness(self): |
307 | + real_bzrdir = self.make_bzrdir('path') |
308 | + class FakeBzrDir(object): |
309 | + def __init__(self): |
310 | + self.calls = [] |
311 | + def open_repository(self): |
312 | + self.calls.append('open_repository') |
313 | + raise errors.NoRepositoryPresent(real_bzrdir) |
314 | + fake_bzrdir = FakeBzrDir() |
315 | + err = errors.NotBranchError('path', bzrdir=fake_bzrdir) |
316 | + self.assertEqual([], fake_bzrdir.calls) |
317 | + str(err) |
318 | + self.assertEqual(['open_repository'], fake_bzrdir.calls) |
319 | + # Stringifying twice doesn't try to open a repository twice. |
320 | + str(err) |
321 | + self.assertEqual(['open_repository'], fake_bzrdir.calls) |
322 | + |
323 | |
324 | class PassThroughError(errors.BzrError): |
325 | |
326 | |
327 | === modified file 'bzrlib/tests/test_remote.py' |
328 | --- bzrlib/tests/test_remote.py 2009-12-16 22:29:31 +0000 |
329 | +++ bzrlib/tests/test_remote.py 2010-01-15 05:14:10 +0000 |
330 | @@ -440,7 +440,7 @@ |
331 | 'BzrDir.cloning_metadir', ('quack/', 'False'), |
332 | 'error', ('BranchReference',)), |
333 | client.add_expected_call( |
334 | - 'BzrDir.open_branchV2', ('quack/',), |
335 | + 'BzrDir.open_branchV3', ('quack/',), |
336 | 'success', ('ref', self.get_url('referenced'))), |
337 | a_bzrdir = RemoteBzrDir(transport, remote.RemoteBzrDirFormat(), |
338 | _client=client) |
339 | @@ -531,7 +531,7 @@ |
340 | self.make_branch('.') |
341 | a_dir = BzrDir.open(self.get_url('.')) |
342 | self.reset_smart_call_log() |
343 | - verb = 'BzrDir.open_branchV2' |
344 | + verb = 'BzrDir.open_branchV3' |
345 | self.disable_verb(verb) |
346 | format = a_dir.open_branch() |
347 | call_count = len([call for call in self.hpss_calls if |
348 | @@ -547,7 +547,7 @@ |
349 | transport = transport.clone('quack') |
350 | client = FakeClient(transport.base) |
351 | client.add_expected_call( |
352 | - 'BzrDir.open_branchV2', ('quack/',), |
353 | + 'BzrDir.open_branchV3', ('quack/',), |
354 | 'success', ('branch', branch_network_name)) |
355 | client.add_expected_call( |
356 | 'BzrDir.find_repositoryV3', ('quack/',), |
357 | @@ -572,7 +572,7 @@ |
358 | _client=client) |
359 | self.assertRaises(errors.NotBranchError, bzrdir.open_branch) |
360 | self.assertEqual( |
361 | - [('call', 'BzrDir.open_branchV2', ('quack/',))], |
362 | + [('call', 'BzrDir.open_branchV3', ('quack/',))], |
363 | client._calls) |
364 | |
365 | def test__get_tree_branch(self): |
366 | @@ -602,7 +602,7 @@ |
367 | network_name = reference_format.network_name() |
368 | branch_network_name = self.get_branch_format().network_name() |
369 | client.add_expected_call( |
370 | - 'BzrDir.open_branchV2', ('~hello/',), |
371 | + 'BzrDir.open_branchV3', ('~hello/',), |
372 | 'success', ('branch', branch_network_name)) |
373 | client.add_expected_call( |
374 | 'BzrDir.find_repositoryV3', ('~hello/',), |
375 | @@ -1190,7 +1190,7 @@ |
376 | client = FakeClient(self.get_url()) |
377 | branch_network_name = self.get_branch_format().network_name() |
378 | client.add_expected_call( |
379 | - 'BzrDir.open_branchV2', ('stacked/',), |
380 | + 'BzrDir.open_branchV3', ('stacked/',), |
381 | 'success', ('branch', branch_network_name)) |
382 | client.add_expected_call( |
383 | 'BzrDir.find_repositoryV3', ('stacked/',), |
384 | @@ -1226,7 +1226,7 @@ |
385 | client = FakeClient(self.get_url()) |
386 | branch_network_name = self.get_branch_format().network_name() |
387 | client.add_expected_call( |
388 | - 'BzrDir.open_branchV2', ('stacked/',), |
389 | + 'BzrDir.open_branchV3', ('stacked/',), |
390 | 'success', ('branch', branch_network_name)) |
391 | client.add_expected_call( |
392 | 'BzrDir.find_repositoryV3', ('stacked/',), |
393 | @@ -2775,6 +2775,15 @@ |
394 | expected_error = errors.NotBranchError(path=bzrdir.root_transport.base) |
395 | self.assertEqual(expected_error, translated_error) |
396 | |
397 | + def test_nobranch_one_arg(self): |
398 | + bzrdir = self.make_bzrdir('') |
399 | + translated_error = self.translateTuple( |
400 | + ('nobranch', 'extra detail'), bzrdir=bzrdir) |
401 | + expected_error = errors.NotBranchError( |
402 | + path=bzrdir.root_transport.base, |
403 | + detail='extra detail') |
404 | + self.assertEqual(expected_error, translated_error) |
405 | + |
406 | def test_LockContention(self): |
407 | translated_error = self.translateTuple(('LockContention',)) |
408 | expected_error = errors.LockContention('(remote lock)') |
409 | |
410 | === modified file 'bzrlib/tests/test_smart.py' |
411 | --- bzrlib/tests/test_smart.py 2009-10-29 00:16:50 +0000 |
412 | +++ bzrlib/tests/test_smart.py 2010-01-15 05:14:10 +0000 |
413 | @@ -524,6 +524,14 @@ |
414 | self.assertEqual(SmartServerResponse(('ok', reference_url)), |
415 | request.execute('reference')) |
416 | |
417 | + def test_notification_on_branch_from_repository(self): |
418 | + """When there is a repository, the error should return details.""" |
419 | + backing = self.get_transport() |
420 | + request = smart.bzrdir.SmartServerRequestOpenBranch(backing) |
421 | + repo = self.make_repository('.') |
422 | + self.assertEqual(SmartServerResponse(('nobranch',)), |
423 | + request.execute('')) |
424 | + |
425 | |
426 | class TestSmartServerRequestOpenBranchV2(TestCaseWithChrootedTransport): |
427 | |
428 | @@ -575,6 +583,74 @@ |
429 | response) |
430 | self.assertLength(1, opened_branches) |
431 | |
432 | + def test_notification_on_branch_from_repository(self): |
433 | + """When there is a repository, the error should return details.""" |
434 | + backing = self.get_transport() |
435 | + request = smart.bzrdir.SmartServerRequestOpenBranchV2(backing) |
436 | + repo = self.make_repository('.') |
437 | + self.assertEqual(SmartServerResponse(('nobranch',)), |
438 | + request.execute('')) |
439 | + |
440 | + |
441 | +class TestSmartServerRequestOpenBranchV3(TestCaseWithChrootedTransport): |
442 | + |
443 | + def test_no_branch(self): |
444 | + """When there is no branch, ('nobranch', ) is returned.""" |
445 | + backing = self.get_transport() |
446 | + self.make_bzrdir('.') |
447 | + request = smart.bzrdir.SmartServerRequestOpenBranchV3(backing) |
448 | + self.assertEqual(SmartServerResponse(('nobranch',)), |
449 | + request.execute('')) |
450 | + |
451 | + def test_branch(self): |
452 | + """When there is a branch, 'ok' is returned.""" |
453 | + backing = self.get_transport() |
454 | + expected = self.make_branch('.')._format.network_name() |
455 | + request = smart.bzrdir.SmartServerRequestOpenBranchV3(backing) |
456 | + self.assertEqual(SuccessfulSmartServerResponse(('branch', expected)), |
457 | + request.execute('')) |
458 | + |
459 | + def test_branch_reference(self): |
460 | + """When there is a branch reference, the reference URL is returned.""" |
461 | + self.vfs_transport_factory = local.LocalURLServer |
462 | + backing = self.get_transport() |
463 | + request = smart.bzrdir.SmartServerRequestOpenBranchV3(backing) |
464 | + branch = self.make_branch('branch') |
465 | + checkout = branch.create_checkout('reference',lightweight=True) |
466 | + reference_url = BranchReferenceFormat().get_reference(checkout.bzrdir) |
467 | + self.assertFileEqual(reference_url, 'reference/.bzr/branch/location') |
468 | + self.assertEqual(SuccessfulSmartServerResponse(('ref', reference_url)), |
469 | + request.execute('reference')) |
470 | + |
471 | + def test_stacked_branch(self): |
472 | + """Opening a stacked branch does not open the stacked-on branch.""" |
473 | + trunk = self.make_branch('trunk') |
474 | + feature = self.make_branch('feature') |
475 | + feature.set_stacked_on_url(trunk.base) |
476 | + opened_branches = [] |
477 | + Branch.hooks.install_named_hook('open', opened_branches.append, None) |
478 | + backing = self.get_transport() |
479 | + request = smart.bzrdir.SmartServerRequestOpenBranchV3(backing) |
480 | + request.setup_jail() |
481 | + try: |
482 | + response = request.execute('feature') |
483 | + finally: |
484 | + request.teardown_jail() |
485 | + expected_format = feature._format.network_name() |
486 | + self.assertEqual( |
487 | + SuccessfulSmartServerResponse(('branch', expected_format)), |
488 | + response) |
489 | + self.assertLength(1, opened_branches) |
490 | + |
491 | + def test_notification_on_branch_from_repository(self): |
492 | + """When there is a repository, the error should return details.""" |
493 | + backing = self.get_transport() |
494 | + request = smart.bzrdir.SmartServerRequestOpenBranchV3(backing) |
495 | + repo = self.make_repository('.') |
496 | + self.assertEqual( |
497 | + SmartServerResponse(('nobranch', 'location is a repository')), |
498 | + request.execute('')) |
499 | + |
500 | |
501 | class TestSmartServerRequestRevisionHistory(tests.TestCaseWithMemoryTransport): |
502 | |
503 | @@ -1771,6 +1847,8 @@ |
504 | smart.bzrdir.SmartServerRequestOpenBranch) |
505 | self.assertHandlerEqual('BzrDir.open_branchV2', |
506 | smart.bzrdir.SmartServerRequestOpenBranchV2) |
507 | + self.assertHandlerEqual('BzrDir.open_branchV3', |
508 | + smart.bzrdir.SmartServerRequestOpenBranchV3) |
509 | self.assertHandlerEqual('PackRepository.autopack', |
510 | smart.packrepository.SmartServerPackRepositoryAutopack) |
511 | self.assertHandlerEqual('Repository.gather_stats', |
This is a substantial reworking of <https:/ /code.edge. launchpad. net/~slyguy/ bzr/bug- 440952- bzrdir/ +merge/ 13431>.
The basic mechanism now is that probing for a repository is delayed until the NotBranchError is stringified. So for non-UI cases, the only extra cost should be that NotBranchError will hold a reference to a bzrdir that is never used.
This also adds a BzrDir. open_branchV3 verb so that this extra field can be passed via the smart protocol. Perhaps the 'nobranch' case should be handled via more generic error translation instead, but this patch was already large enough.
I think this resolves the various objections raised in earlier reviews: it doesn't probe unnecessarily, it doesn't add a has_repository method, it doesn't break HPSS backwards compatibility, etc. I'm willing to be proven wrong, of course! So, please review :)