Merge lp:~jelmer/brz/hpss-working-tree-update into lp:brz
- hpss-working-tree-update
- Merge into trunk
Proposed by
Jelmer Vernooij
Status: | Work in progress | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~jelmer/brz/hpss-working-tree-update | ||||
Merge into: | lp:brz | ||||
Diff against target: |
555 lines (+373/-11) 11 files modified
breezy/bzr/bzrdir.py (+2/-2) breezy/bzr/remote.py (+168/-2) breezy/bzr/smart/branch.py (+1/-1) breezy/bzr/smart/request.py (+6/-0) breezy/bzr/smart/workingtree.py (+115/-0) breezy/bzr/workingtree_4.py (+2/-2) breezy/controldir.py (+3/-2) breezy/tests/blackbox/test_push.py (+17/-0) breezy/tests/test_remote.py (+54/-0) breezy/workingtree.py (+2/-2) doc/en/release-notes/brz-3.0.txt (+3/-0) |
||||
To merge this branch: | bzr merge lp:~jelmer/brz/hpss-working-tree-update | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Breezy developers | Pending | ||
Review via email: mp+345970@code.launchpad.net |
Commit message
Add a HPSS call for updating the remote working tree.
Description of the change
Add a HPSS call for updating the remote working tree.
To post a comment you must log in.
Unmerged revisions
- 6324. By Jelmer Vernooij
-
Pass along remote argument.
- 6323. By Jelmer Vernooij
-
Fix some tests.
- 6322. By Jelmer Vernooij
-
merge trunk.
- 6321. By Jelmer Vernooij
-
Update NEWS.
- 6320. By Jelmer Vernooij
-
Add test for push updating remote working tree.
- 6319. By Jelmer Vernooij
-
Add server side.
- 6318. By Jelmer Vernooij
-
Merge hpss-working-tree.
- 6317. By Jelmer Vernooij
-
Add basic RemoteWorkingTr
ee.update. - 6316. By Jelmer Vernooij
-
Support opening remote working trees.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'breezy/bzr/bzrdir.py' | |||
2 | --- breezy/bzr/bzrdir.py 2018-05-07 14:35:05 +0000 | |||
3 | +++ breezy/bzr/bzrdir.py 2018-05-19 02:48:08 +0000 | |||
4 | @@ -1103,13 +1103,13 @@ | |||
5 | 1103 | return format.open(self, _found=True) | 1103 | return format.open(self, _found=True) |
6 | 1104 | 1104 | ||
7 | 1105 | def open_workingtree(self, unsupported=False, | 1105 | def open_workingtree(self, unsupported=False, |
9 | 1106 | recommend_upgrade=True): | 1106 | recommend_upgrade=True, remote=False): |
10 | 1107 | """See BzrDir.open_workingtree.""" | 1107 | """See BzrDir.open_workingtree.""" |
11 | 1108 | from .workingtree import WorkingTreeFormatMetaDir | 1108 | from .workingtree import WorkingTreeFormatMetaDir |
12 | 1109 | format = WorkingTreeFormatMetaDir.find_format(self) | 1109 | format = WorkingTreeFormatMetaDir.find_format(self) |
13 | 1110 | format.check_support_status(unsupported, recommend_upgrade, | 1110 | format.check_support_status(unsupported, recommend_upgrade, |
14 | 1111 | basedir=self.root_transport.base) | 1111 | basedir=self.root_transport.base) |
16 | 1112 | return format.open(self, _found=True) | 1112 | return format.open(self, remote=remote, _found=True) |
17 | 1113 | 1113 | ||
18 | 1114 | def _get_config(self): | 1114 | def _get_config(self): |
19 | 1115 | return config.TransportConfig(self.transport, 'control.conf') | 1115 | return config.TransportConfig(self.transport, 'control.conf') |
20 | 1116 | 1116 | ||
21 | === modified file 'breezy/bzr/remote.py' | |||
22 | --- breezy/bzr/remote.py 2018-05-13 16:23:17 +0000 | |||
23 | +++ breezy/bzr/remote.py 2018-05-19 02:48:08 +0000 | |||
24 | @@ -45,6 +45,7 @@ | |||
25 | 45 | inventory_delta, | 45 | inventory_delta, |
26 | 46 | vf_repository, | 46 | vf_repository, |
27 | 47 | vf_search, | 47 | vf_search, |
28 | 48 | workingtree, | ||
29 | 48 | ) | 49 | ) |
30 | 49 | from .branch import BranchReferenceFormat | 50 | from .branch import BranchReferenceFormat |
31 | 50 | from ..branch import BranchWriteLockResult | 51 | from ..branch import BranchWriteLockResult |
32 | @@ -883,9 +884,12 @@ | |||
33 | 883 | self._has_working_tree = (response[0] == b'yes') | 884 | self._has_working_tree = (response[0] == b'yes') |
34 | 884 | return self._has_working_tree | 885 | return self._has_working_tree |
35 | 885 | 886 | ||
37 | 886 | def open_workingtree(self, recommend_upgrade=True): | 887 | def open_workingtree(self, recommend_upgrade=True, remote=False): |
38 | 887 | if self.has_workingtree(): | 888 | if self.has_workingtree(): |
40 | 888 | raise errors.NotLocalUrl(self.root_transport) | 889 | if remote: |
41 | 890 | return RemoteWorkingTree(self, self.open_branch()) | ||
42 | 891 | else: | ||
43 | 892 | raise errors.NotLocalUrl(self.root_transport.base) | ||
44 | 889 | else: | 893 | else: |
45 | 890 | raise errors.NoWorkingTree(self.root_transport.base) | 894 | raise errors.NoWorkingTree(self.root_transport.base) |
46 | 891 | 895 | ||
47 | @@ -4127,6 +4131,168 @@ | |||
48 | 4127 | return self._bzrdir._real_bzrdir | 4131 | return self._bzrdir._real_bzrdir |
49 | 4128 | 4132 | ||
50 | 4129 | 4133 | ||
51 | 4134 | class RemoteWorkingTreeFormat(workingtree.WorkingTreeFormat): | ||
52 | 4135 | |||
53 | 4136 | def __init__(self): | ||
54 | 4137 | super(RemoteWorkingTreeFormat, self).__init__() | ||
55 | 4138 | |||
56 | 4139 | def get_format_description(self): | ||
57 | 4140 | return "Remote Working Tree" | ||
58 | 4141 | |||
59 | 4142 | def __eq__(self, other): | ||
60 | 4143 | return (isinstance(other, RemoteWorkingTreeFormat) and | ||
61 | 4144 | self.__dict__ == other.__dict__) | ||
62 | 4145 | |||
63 | 4146 | |||
64 | 4147 | class RemoteWorkingTree(workingtree.WorkingTree, _RpcHelper, lock._RelockDebugMixin): | ||
65 | 4148 | """Working tree stored on a server accessed by HPSS RPC. | ||
66 | 4149 | |||
67 | 4150 | At the moment this only implements .update(). All other methods | ||
68 | 4151 | raise _ensure_real(). | ||
69 | 4152 | """ | ||
70 | 4153 | |||
71 | 4154 | def __init__(self, remote_controldir, remote_branch, _client=None): | ||
72 | 4155 | self.controldir = remote_controldir | ||
73 | 4156 | self._branch = remote_branch | ||
74 | 4157 | self._format = RemoteWorkingTreeFormat() | ||
75 | 4158 | self._real_workingtree = None | ||
76 | 4159 | self._lock_mode = None | ||
77 | 4160 | self._lock_count = 0 | ||
78 | 4161 | if _client is None: | ||
79 | 4162 | self._client = remote_controldir._client | ||
80 | 4163 | else: | ||
81 | 4164 | self._client = _client | ||
82 | 4165 | |||
83 | 4166 | @property | ||
84 | 4167 | def user_transport(self): | ||
85 | 4168 | return self.controldir.user_transport | ||
86 | 4169 | |||
87 | 4170 | @property | ||
88 | 4171 | def control_transport(self): | ||
89 | 4172 | # XXX: Normally you shouldn't directly get at the remote repository | ||
90 | 4173 | # transport, but I'm not sure it's worth making this method | ||
91 | 4174 | # optional -- mbp 2010-04-21 | ||
92 | 4175 | return self.controldir.get_repository_transport(None) | ||
93 | 4176 | |||
94 | 4177 | def _translate_error(self, err, **context): | ||
95 | 4178 | _translate_error(err, workingtree=self, **context) | ||
96 | 4179 | |||
97 | 4180 | def _ensure_real(self): | ||
98 | 4181 | # Most working tree operations require local access at the moment. | ||
99 | 4182 | # When Bazaar working trees can be accessed over | ||
100 | 4183 | # transports, set self._real_workingtree here. | ||
101 | 4184 | raise errors.NotLocalUrl(self.controldir.root_transport) | ||
102 | 4185 | |||
103 | 4186 | def is_locked(self): | ||
104 | 4187 | return self._lock_count >= 1 | ||
105 | 4188 | |||
106 | 4189 | def lock_read(self): | ||
107 | 4190 | """Lock the working tree for read operations. | ||
108 | 4191 | |||
109 | 4192 | :return: A bzrlib.lock.LogicalLockResult. | ||
110 | 4193 | """ | ||
111 | 4194 | # wrong eventually - want a local lock cache context | ||
112 | 4195 | if not self._lock_mode: | ||
113 | 4196 | self._note_lock('r') | ||
114 | 4197 | self._lock_mode = 'r' | ||
115 | 4198 | self._lock_count = 1 | ||
116 | 4199 | if self._real_workingtree is not None: | ||
117 | 4200 | self._real_workingtree.lock_read() | ||
118 | 4201 | self.branch.lock_read() | ||
119 | 4202 | else: | ||
120 | 4203 | self._lock_count += 1 | ||
121 | 4204 | return lock.LogicalLockResult(self.unlock) | ||
122 | 4205 | |||
123 | 4206 | def lock_write(self): | ||
124 | 4207 | if not self._lock_mode: | ||
125 | 4208 | self._note_lock('w') | ||
126 | 4209 | if self._real_workingtree is not None: | ||
127 | 4210 | self._real_workingtree.lock_write() | ||
128 | 4211 | self._lock_mode = 'w' | ||
129 | 4212 | self._lock_count = 1 | ||
130 | 4213 | self.branch.lock_write() | ||
131 | 4214 | elif self._lock_mode == 'r': | ||
132 | 4215 | raise errors.ReadOnlyError(self) | ||
133 | 4216 | else: | ||
134 | 4217 | self._lock_count += 1 | ||
135 | 4218 | return lock.LogicalLockResult( | ||
136 | 4219 | lambda: self.unlock()) | ||
137 | 4220 | |||
138 | 4221 | @only_raises(errors.LockNotHeld, errors.LockBroken) | ||
139 | 4222 | def unlock(self): | ||
140 | 4223 | if not self._lock_count: | ||
141 | 4224 | return lock.cant_unlock_not_held(self) | ||
142 | 4225 | self._lock_count -= 1 | ||
143 | 4226 | if self._lock_count > 0: | ||
144 | 4227 | return | ||
145 | 4228 | old_mode = self._lock_mode | ||
146 | 4229 | self._lock_mode = None | ||
147 | 4230 | try: | ||
148 | 4231 | if self._real_workingtree is not None: | ||
149 | 4232 | self._real_workingtree.unlock() | ||
150 | 4233 | finally: | ||
151 | 4234 | self.branch.unlock() | ||
152 | 4235 | |||
153 | 4236 | def get_physical_lock_status(self): | ||
154 | 4237 | """See WorkingTree.get_physical_lock_status().""" | ||
155 | 4238 | path = self.controldir._path_for_remote_call(self._client) | ||
156 | 4239 | try: | ||
157 | 4240 | response = self._call(b'WorkingTree.get_physical_lock_status', path) | ||
158 | 4241 | except errors.UnknownSmartMethod: | ||
159 | 4242 | self._ensure_real() | ||
160 | 4243 | return self._real_workingtree.get_physical_lock_status() | ||
161 | 4244 | if response[0] not in (b'yes', b'no'): | ||
162 | 4245 | raise errors.UnexpectedSmartServerResponse(response) | ||
163 | 4246 | return (response[0] == b'yes') | ||
164 | 4247 | |||
165 | 4248 | def _update_rpc(self, old_tip=None, change_reporter=None, | ||
166 | 4249 | possible_transports=None, revision=None, show_base=False): | ||
167 | 4250 | medium = self.controldir._client._medium | ||
168 | 4251 | path = self.controldir._path_for_remote_call(self.controldir._client) | ||
169 | 4252 | if revision is None: | ||
170 | 4253 | revision = '' | ||
171 | 4254 | if old_tip is None: | ||
172 | 4255 | old_tip = '' | ||
173 | 4256 | response, response_handler = self._call_expecting_body( | ||
174 | 4257 | 'WorkingTree.update', path, revision, old_tip, show_base) | ||
175 | 4258 | if response[0] != 'ok': | ||
176 | 4259 | raise errors.UnexpectedSmartServerResponse(response) | ||
177 | 4260 | if change_reporter is None: | ||
178 | 4261 | response_handler.cancel_read_body() | ||
179 | 4262 | else: | ||
180 | 4263 | byte_stream = response_handler.read_streamed_body() | ||
181 | 4264 | data = "" | ||
182 | 4265 | for bytes in byte_stream: | ||
183 | 4266 | data += bytes | ||
184 | 4267 | lines = bytes.split("\n") | ||
185 | 4268 | data = lines.pop() | ||
186 | 4269 | for line in lines: | ||
187 | 4270 | if not line.startswith("change: "): | ||
188 | 4271 | raise errors.UnexpectedSmartServerResponse(response) | ||
189 | 4272 | change = bencode.bdecode_as_tuple(line[8:]) | ||
190 | 4273 | # FIXME: More post-processing | ||
191 | 4274 | change_reporter.report(*change) | ||
192 | 4275 | return int(response[1]) | ||
193 | 4276 | |||
194 | 4277 | def update(self, old_tip=None, change_reporter=None, | ||
195 | 4278 | possible_transports=None, revision=None, show_base=False): | ||
196 | 4279 | try: | ||
197 | 4280 | return self._update_rpc(old_tip=old_tip, | ||
198 | 4281 | change_reporter=change_reporter, | ||
199 | 4282 | possible_transports=possible_transports, revision=revision, | ||
200 | 4283 | show_base=show_base) | ||
201 | 4284 | except errors.UnknownSmartMethod: | ||
202 | 4285 | self._ensure_real() | ||
203 | 4286 | return self._real_workingtree.update(old_tip=old_tip, | ||
204 | 4287 | change_reporter=change_reporter, | ||
205 | 4288 | possible_transports=possible_transports, | ||
206 | 4289 | revision=revision, show_base=show_base) | ||
207 | 4290 | |||
208 | 4291 | def get_parent_ids(self): | ||
209 | 4292 | self._ensure_real() | ||
210 | 4293 | return self._real_workingtree.get_parent_ids() | ||
211 | 4294 | |||
212 | 4295 | |||
213 | 4130 | error_translators = registry.Registry() | 4296 | error_translators = registry.Registry() |
214 | 4131 | no_context_error_translators = registry.Registry() | 4297 | no_context_error_translators = registry.Registry() |
215 | 4132 | 4298 | ||
216 | 4133 | 4299 | ||
217 | === modified file 'breezy/bzr/smart/branch.py' | |||
218 | --- breezy/bzr/smart/branch.py 2018-05-13 02:18:13 +0000 | |||
219 | +++ breezy/bzr/smart/branch.py 2018-05-19 02:48:08 +0000 | |||
220 | @@ -14,7 +14,7 @@ | |||
221 | 14 | # along with this program; if not, write to the Free Software | 14 | # along with this program; if not, write to the Free Software |
222 | 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
223 | 16 | 16 | ||
225 | 17 | """Server-side branch related request implmentations.""" | 17 | """Server-side branch related request implementations.""" |
226 | 18 | 18 | ||
227 | 19 | from __future__ import absolute_import | 19 | from __future__ import absolute_import |
228 | 20 | 20 | ||
229 | 21 | 21 | ||
230 | === modified file 'breezy/bzr/smart/request.py' | |||
231 | --- breezy/bzr/smart/request.py 2018-05-13 02:18:13 +0000 | |||
232 | +++ breezy/bzr/smart/request.py 2018-05-19 02:48:08 +0000 | |||
233 | @@ -791,3 +791,9 @@ | |||
234 | 791 | request_handlers.register_lazy( | 791 | request_handlers.register_lazy( |
235 | 792 | b'Transport.is_readonly', 'breezy.bzr.smart.request', | 792 | b'Transport.is_readonly', 'breezy.bzr.smart.request', |
236 | 793 | 'SmartServerIsReadonly', info='read') | 793 | 'SmartServerIsReadonly', info='read') |
237 | 794 | request_handlers.register_lazy( | ||
238 | 795 | b'WorkingTree.update', 'breezy.bzr.smart.workingtree', | ||
239 | 796 | 'SmartServerWorkingTreeUpdate', info='mutate') | ||
240 | 797 | request_handlers.register_lazy( | ||
241 | 798 | b'WorkingTree.get_physical_lock_status', 'breezy.bzr.smart.workingtree', | ||
242 | 799 | 'SmartServerWorkingTreeGetPhysicalLockStatus', info='read') | ||
243 | 794 | 800 | ||
244 | === added file 'breezy/bzr/smart/workingtree.py' | |||
245 | --- breezy/bzr/smart/workingtree.py 1970-01-01 00:00:00 +0000 | |||
246 | +++ breezy/bzr/smart/workingtree.py 2018-05-19 02:48:08 +0000 | |||
247 | @@ -0,0 +1,115 @@ | |||
248 | 1 | # Copyright (C) 2011 Canonical Ltd | ||
249 | 2 | # | ||
250 | 3 | # This program is free software; you can redistribute it and/or modify | ||
251 | 4 | # it under the terms of the GNU General Public License as published by | ||
252 | 5 | # the Free Software Foundation; either version 2 of the License, or | ||
253 | 6 | # (at your option) any later version. | ||
254 | 7 | # | ||
255 | 8 | # This program is distributed in the hope that it will be useful, | ||
256 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
257 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
258 | 11 | # GNU General Public License for more details. | ||
259 | 12 | # | ||
260 | 13 | # You should have received a copy of the GNU General Public License | ||
261 | 14 | # along with this program; if not, write to the Free Software | ||
262 | 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
263 | 16 | |||
264 | 17 | """Server-side working tree related request implementations.""" | ||
265 | 18 | |||
266 | 19 | from __future__ import absolute_import | ||
267 | 20 | |||
268 | 21 | |||
269 | 22 | from breezy import ( | ||
270 | 23 | bencode, | ||
271 | 24 | delta as _mod_delta, | ||
272 | 25 | errors, | ||
273 | 26 | revision as _mod_revision, | ||
274 | 27 | ) | ||
275 | 28 | from breezy.controldir import ControlDir | ||
276 | 29 | from breezy.bzr.smart.request import ( | ||
277 | 30 | FailedSmartServerResponse, | ||
278 | 31 | SmartServerRequest, | ||
279 | 32 | SuccessfulSmartServerResponse, | ||
280 | 33 | ) | ||
281 | 34 | |||
282 | 35 | |||
283 | 36 | class SmartServerWorkingTreeRequest(SmartServerRequest): | ||
284 | 37 | """Base class for handling common working tree request logic. | ||
285 | 38 | """ | ||
286 | 39 | |||
287 | 40 | def do(self, path, *args): | ||
288 | 41 | """Execute a request for a working tree at path. | ||
289 | 42 | |||
290 | 43 | All working tree requests take a path to the tree as their first | ||
291 | 44 | argument. | ||
292 | 45 | |||
293 | 46 | :param path: The path for the repository as received from the | ||
294 | 47 | client. | ||
295 | 48 | :return: A SmartServerResponse from self.do_with_workingtree(). | ||
296 | 49 | """ | ||
297 | 50 | transport = self.transport_from_client_path(path) | ||
298 | 51 | controldir = ControlDir.open_from_transport(transport) | ||
299 | 52 | workingtree = controldir.open_workingtree(remote=True) | ||
300 | 53 | return self.do_with_workingtree(workingtree, *args) | ||
301 | 54 | |||
302 | 55 | def do_with_workingtree(self, workingtree, *args): | ||
303 | 56 | raise NotImplementedError(self.do_with_workingtree) | ||
304 | 57 | |||
305 | 58 | |||
306 | 59 | class SerializingChangeReporter(_mod_delta._ChangeReporter): | ||
307 | 60 | """Change reporter that serializes.""" | ||
308 | 61 | |||
309 | 62 | def __init__(self): | ||
310 | 63 | self.chunks = [] | ||
311 | 64 | |||
312 | 65 | def _format_path(self, path): | ||
313 | 66 | if path is None: | ||
314 | 67 | return "" | ||
315 | 68 | elif path == "": | ||
316 | 69 | return "." | ||
317 | 70 | else: | ||
318 | 71 | return path.encode("utf-8") | ||
319 | 72 | |||
320 | 73 | def _format_kind(self, kind): | ||
321 | 74 | if kind is None: | ||
322 | 75 | return "" | ||
323 | 76 | return kind | ||
324 | 77 | |||
325 | 78 | def report(self, file_id, paths, versioned, renamed, modified, exe_change, | ||
326 | 79 | kind): | ||
327 | 80 | self.chunks.append("report: %s\n" % bencode.bencode((file_id, | ||
328 | 81 | self._format_path(paths[0]), self._format_path(paths[1]), | ||
329 | 82 | versioned, renamed, modified, exe_change, | ||
330 | 83 | self._format_kind(kind[0]), self._format_kind(kind[1])))) | ||
331 | 84 | |||
332 | 85 | |||
333 | 86 | class SmartServerWorkingTreeUpdate(SmartServerWorkingTreeRequest): | ||
334 | 87 | """Update a working tree. | ||
335 | 88 | |||
336 | 89 | New in 3.0. | ||
337 | 90 | """ | ||
338 | 91 | |||
339 | 92 | def do_with_workingtree(self, workingtree, revision, old_tip, show_base): | ||
340 | 93 | if revision == '': | ||
341 | 94 | revision = None | ||
342 | 95 | if old_tip == '': | ||
343 | 96 | old_tip = None | ||
344 | 97 | reporter = SerializingChangeReporter() | ||
345 | 98 | with workingtree.lock_write(): # TODO(jelmer): Accept token | ||
346 | 99 | nconflicts = workingtree.update(change_reporter=reporter, | ||
347 | 100 | revision=revision, old_tip=old_tip, show_base=show_base) | ||
348 | 101 | return SuccessfulSmartServerResponse(('ok', nconflicts), | ||
349 | 102 | body_stream=iter(reporter.chunks)) | ||
350 | 103 | |||
351 | 104 | |||
352 | 105 | class SmartServerWorkingTreeGetPhysicalLockStatus(SmartServerWorkingTreeRequest): | ||
353 | 106 | """Get the physical lock status for a working tree. | ||
354 | 107 | |||
355 | 108 | New in 3.0. | ||
356 | 109 | """ | ||
357 | 110 | |||
358 | 111 | def do_with_workingtree(self, workingtree): | ||
359 | 112 | if workingtree.get_physical_lock_status(): | ||
360 | 113 | return SuccessfulSmartServerResponse((b'yes', )) | ||
361 | 114 | else: | ||
362 | 115 | return SuccessfulSmartServerResponse((b'no', )) | ||
363 | 0 | 116 | ||
364 | === modified file 'breezy/bzr/workingtree_4.py' | |||
365 | --- breezy/bzr/workingtree_4.py 2018-03-28 01:17:12 +0000 | |||
366 | +++ breezy/bzr/workingtree_4.py 2018-05-19 02:48:08 +0000 | |||
367 | @@ -1567,7 +1567,7 @@ | |||
368 | 1567 | :param wt: the WorkingTree object | 1567 | :param wt: the WorkingTree object |
369 | 1568 | """ | 1568 | """ |
370 | 1569 | 1569 | ||
372 | 1570 | def open(self, a_controldir, _found=False): | 1570 | def open(self, a_controldir, remote=False, _found=False): |
373 | 1571 | """Return the WorkingTree object for a_controldir | 1571 | """Return the WorkingTree object for a_controldir |
374 | 1572 | 1572 | ||
375 | 1573 | _found is a private parameter, do not use it. It is used to indicate | 1573 | _found is a private parameter, do not use it. It is used to indicate |
376 | @@ -1576,7 +1576,7 @@ | |||
377 | 1576 | if not _found: | 1576 | if not _found: |
378 | 1577 | # we are being called directly and must probe. | 1577 | # we are being called directly and must probe. |
379 | 1578 | raise NotImplementedError | 1578 | raise NotImplementedError |
381 | 1579 | if not isinstance(a_controldir.transport, LocalTransport): | 1579 | if not remote and not isinstance(a_controldir.transport, LocalTransport): |
382 | 1580 | raise errors.NotLocalUrl(a_controldir.transport.base) | 1580 | raise errors.NotLocalUrl(a_controldir.transport.base) |
383 | 1581 | wt = self._open(a_controldir, self._open_control_files(a_controldir)) | 1581 | wt = self._open(a_controldir, self._open_control_files(a_controldir)) |
384 | 1582 | return wt | 1582 | return wt |
385 | 1583 | 1583 | ||
386 | === modified file 'breezy/controldir.py' | |||
387 | --- breezy/controldir.py 2018-05-07 14:35:05 +0000 | |||
388 | +++ breezy/controldir.py 2018-05-19 02:48:08 +0000 | |||
389 | @@ -306,6 +306,7 @@ | |||
390 | 306 | (but still fully supported). | 306 | (but still fully supported). |
391 | 307 | :param from_branch: override controldir branch (for lightweight | 307 | :param from_branch: override controldir branch (for lightweight |
392 | 308 | checkouts) | 308 | checkouts) |
393 | 309 | :param remote: open working tree even if it's remote | ||
394 | 309 | """ | 310 | """ |
395 | 310 | raise NotImplementedError(self.open_workingtree) | 311 | raise NotImplementedError(self.open_workingtree) |
396 | 311 | 312 | ||
397 | @@ -444,9 +445,9 @@ | |||
398 | 444 | # FIXME: Should be done only if we succeed ? -- vila 2012-01-18 | 445 | # FIXME: Should be done only if we succeed ? -- vila 2012-01-18 |
399 | 445 | source.set_push_location(br_to.base) | 446 | source.set_push_location(br_to.base) |
400 | 446 | try: | 447 | try: |
402 | 447 | tree_to = self.open_workingtree() | 448 | tree_to = self.open_workingtree(remote=True) |
403 | 448 | except errors.NotLocalUrl: | 449 | except errors.NotLocalUrl: |
405 | 449 | push_result.branch_push_result = source.push(br_to, | 450 | push_result.branch_push_result = source.push(br_to, |
406 | 450 | overwrite, stop_revision=revision_id, lossy=lossy) | 451 | overwrite, stop_revision=revision_id, lossy=lossy) |
407 | 451 | push_result.workingtree_updated = False | 452 | push_result.workingtree_updated = False |
408 | 452 | except errors.NoWorkingTree: | 453 | except errors.NoWorkingTree: |
409 | 453 | 454 | ||
410 | === modified file 'breezy/tests/blackbox/test_push.py' | |||
411 | --- breezy/tests/blackbox/test_push.py 2018-03-26 00:54:10 +0000 | |||
412 | +++ breezy/tests/blackbox/test_push.py 2018-05-19 02:48:08 +0000 | |||
413 | @@ -266,6 +266,23 @@ | |||
414 | 266 | # ancestry | 266 | # ancestry |
415 | 267 | self.assertEqual([('A',), ('C',)], sorted(target_repo.revisions.keys())) | 267 | self.assertEqual([('A',), ('C',)], sorted(target_repo.revisions.keys())) |
416 | 268 | 268 | ||
417 | 269 | def test_push_smart_updates_tree(self): | ||
418 | 270 | self.setup_smart_server_with_call_log() | ||
419 | 271 | t = self.make_branch_and_tree('from') | ||
420 | 272 | self.build_tree(['from/afile']) | ||
421 | 273 | t.add(['afile']) | ||
422 | 274 | self.make_branch_and_tree('to') | ||
423 | 275 | t.commit(allow_pointless=True, message='first commit') | ||
424 | 276 | self.reset_smart_call_log() | ||
425 | 277 | self.run_bzr(['push', self.get_url('to')], working_dir='from') | ||
426 | 278 | # This figure represent the amount of work to perform this use case. It | ||
427 | 279 | # is entirely ok to reduce this number if a test fails due to rpc_count | ||
428 | 280 | # being too low. If rpc_count increases, more network roundtrips have | ||
429 | 281 | # become necessary for this use case. Please do not adjust this number | ||
430 | 282 | # upwards without agreement from bzr's network support maintainers. | ||
431 | 283 | self.assertLength(11, self.hpss_calls) | ||
432 | 284 | self.assertPathExists('to/afile') | ||
433 | 285 | |||
434 | 269 | def test_push_smart_non_stacked_streaming_acceptance(self): | 286 | def test_push_smart_non_stacked_streaming_acceptance(self): |
435 | 270 | self.setup_smart_server_with_call_log() | 287 | self.setup_smart_server_with_call_log() |
436 | 271 | t = self.make_branch_and_tree('from') | 288 | t = self.make_branch_and_tree('from') |
437 | 272 | 289 | ||
438 | === modified file 'breezy/tests/test_remote.py' | |||
439 | --- breezy/tests/test_remote.py 2018-05-13 02:45:58 +0000 | |||
440 | +++ breezy/tests/test_remote.py 2018-05-19 02:48:08 +0000 | |||
441 | @@ -60,6 +60,7 @@ | |||
442 | 60 | RemoteBranchFormat, | 60 | RemoteBranchFormat, |
443 | 61 | RemoteBzrDir, | 61 | RemoteBzrDir, |
444 | 62 | RemoteBzrDirFormat, | 62 | RemoteBzrDirFormat, |
445 | 63 | RemoteWorkingTree, | ||
446 | 63 | RemoteRepository, | 64 | RemoteRepository, |
447 | 64 | RemoteRepositoryFormat, | 65 | RemoteRepositoryFormat, |
448 | 65 | ) | 66 | ) |
449 | @@ -659,11 +660,24 @@ | |||
450 | 659 | client, transport = self.make_fake_client_and_transport() | 660 | client, transport = self.make_fake_client_and_transport() |
451 | 660 | client.add_expected_call( | 661 | client.add_expected_call( |
452 | 661 | b'BzrDir.open_2.1', (b'quack/',), b'success', (b'yes', b'yes')) | 662 | b'BzrDir.open_2.1', (b'quack/',), b'success', (b'yes', b'yes')) |
453 | 663 | branch_network_name = self.get_branch_format().network_name() | ||
454 | 664 | repo_network_name = self.get_repo_format().network_name() | ||
455 | 665 | client.add_expected_call( | ||
456 | 666 | 'BzrDir.open_branchV3', ('quack/',), | ||
457 | 667 | 'success', ('branch', branch_network_name)) | ||
458 | 668 | client.add_expected_call( | ||
459 | 669 | 'BzrDir.find_repositoryV3', ('quack/',), | ||
460 | 670 | 'success', ('ok', '', 'no', 'no', 'no', repo_network_name)) | ||
461 | 671 | client.add_expected_call( | ||
462 | 672 | 'Branch.get_stacked_on_url', ('quack/',), | ||
463 | 673 | 'error', ('NotStacked',)) | ||
464 | 662 | bd = RemoteBzrDir(transport, RemoteBzrDirFormat(), | 674 | bd = RemoteBzrDir(transport, RemoteBzrDirFormat(), |
465 | 663 | _client=client, _force_probe=True) | 675 | _client=client, _force_probe=True) |
466 | 664 | self.assertIsInstance(bd, RemoteBzrDir) | 676 | self.assertIsInstance(bd, RemoteBzrDir) |
467 | 665 | self.assertTrue(bd.has_workingtree()) | 677 | self.assertTrue(bd.has_workingtree()) |
468 | 666 | self.assertRaises(errors.NotLocalUrl, bd.open_workingtree) | 678 | self.assertRaises(errors.NotLocalUrl, bd.open_workingtree) |
469 | 679 | tree = bd.open_workingtree(remote=True) | ||
470 | 680 | self.assertIsInstance(tree, RemoteWorkingTree) | ||
471 | 667 | self.assertFinished(client) | 681 | self.assertFinished(client) |
472 | 668 | 682 | ||
473 | 669 | def test_backwards_compat(self): | 683 | def test_backwards_compat(self): |
474 | @@ -4261,6 +4275,46 @@ | |||
475 | 4261 | repo.pack([b'hinta', b'hintb']) | 4275 | repo.pack([b'hinta', b'hintb']) |
476 | 4262 | 4276 | ||
477 | 4263 | 4277 | ||
478 | 4278 | class RemoteWorkingTreeTestCase(RemoteBranchTestCase): | ||
479 | 4279 | |||
480 | 4280 | def make_remote_working_tree(self, transport, client): | ||
481 | 4281 | """Make a RemoteWorkingTree using 'client' as its _SmartClient. | ||
482 | 4282 | """ | ||
483 | 4283 | bzrdir = self.make_remote_bzrdir(transport, client) | ||
484 | 4284 | return RemoteWorkingTree(bzrdir, bzrdir.open_branch()) | ||
485 | 4285 | |||
486 | 4286 | |||
487 | 4287 | class TestWorkingTreeLocking(RemoteWorkingTreeTestCase): | ||
488 | 4288 | |||
489 | 4289 | def test_lock(self): | ||
490 | 4290 | transport = MemoryTransport() | ||
491 | 4291 | client = FakeClient(transport.base) | ||
492 | 4292 | branch_network_name = self.get_branch_format().network_name() | ||
493 | 4293 | repo_network_name = self.get_repo_format().network_name() | ||
494 | 4294 | client.add_expected_call( | ||
495 | 4295 | 'BzrDir.open_branchV3', ('.',), | ||
496 | 4296 | 'success', ('branch', branch_network_name)) | ||
497 | 4297 | client.add_expected_call( | ||
498 | 4298 | 'BzrDir.find_repositoryV3', ('.',), | ||
499 | 4299 | 'success', ('ok', '', 'no', 'no', 'no', repo_network_name)) | ||
500 | 4300 | client.add_expected_call( | ||
501 | 4301 | 'Branch.get_stacked_on_url', ('.',), | ||
502 | 4302 | 'error', ('NotStacked',)) | ||
503 | 4303 | client.add_expected_call( | ||
504 | 4304 | 'Branch.lock_write', ('.', '', ''), | ||
505 | 4305 | 'success', ('ok', 'repo token', 'branch token')) | ||
506 | 4306 | client.add_expected_call( | ||
507 | 4307 | 'Branch.unlock', ('.', 'repo token', 'branch token'), | ||
508 | 4308 | 'success', ('ok', )) | ||
509 | 4309 | tree = self.make_remote_working_tree(transport, client) | ||
510 | 4310 | self.assertFalse(tree.is_locked()) | ||
511 | 4311 | tree.lock_write() | ||
512 | 4312 | self.assertTrue(tree.is_locked()) | ||
513 | 4313 | tree.unlock() | ||
514 | 4314 | self.assertFalse(tree.is_locked()) | ||
515 | 4315 | self.assertFinished(client) | ||
516 | 4316 | |||
517 | 4317 | |||
518 | 4264 | class TestRepositoryIterInventories(TestRemoteRepository): | 4318 | class TestRepositoryIterInventories(TestRemoteRepository): |
519 | 4265 | """Test Repository.iter_inventories.""" | 4319 | """Test Repository.iter_inventories.""" |
520 | 4266 | 4320 | ||
521 | 4267 | 4321 | ||
522 | === modified file 'breezy/workingtree.py' | |||
523 | --- breezy/workingtree.py 2018-03-25 00:39:16 +0000 | |||
524 | +++ breezy/workingtree.py 2018-05-19 02:48:08 +0000 | |||
525 | @@ -406,7 +406,7 @@ | |||
526 | 406 | else: | 406 | else: |
527 | 407 | parents = [last_rev] | 407 | parents = [last_rev] |
528 | 408 | try: | 408 | try: |
530 | 409 | merges_bytes = self._transport.get_bytes('pending-merges') | 409 | merges_bytes = self.control_transport.get_bytes('pending-merges') |
531 | 410 | except errors.NoSuchFile: | 410 | except errors.NoSuchFile: |
532 | 411 | pass | 411 | pass |
533 | 412 | else: | 412 | else: |
534 | @@ -584,7 +584,7 @@ | |||
535 | 584 | 584 | ||
536 | 585 | def _set_merges_from_parent_ids(self, parent_ids): | 585 | def _set_merges_from_parent_ids(self, parent_ids): |
537 | 586 | merges = parent_ids[1:] | 586 | merges = parent_ids[1:] |
539 | 587 | self._transport.put_bytes('pending-merges', b'\n'.join(merges), | 587 | self.control_transport.put_bytes('pending-merges', b'\n'.join(merges), |
540 | 588 | mode=self.controldir._get_file_mode()) | 588 | mode=self.controldir._get_file_mode()) |
541 | 589 | 589 | ||
542 | 590 | def _filter_parent_ids_by_ancestry(self, revision_ids): | 590 | def _filter_parent_ids_by_ancestry(self, revision_ids): |
543 | 591 | 591 | ||
544 | === modified file 'doc/en/release-notes/brz-3.0.txt' | |||
545 | --- doc/en/release-notes/brz-3.0.txt 2018-03-24 03:03:03 +0000 | |||
546 | +++ doc/en/release-notes/brz-3.0.txt 2018-05-19 02:48:08 +0000 | |||
547 | @@ -126,6 +126,9 @@ | |||
548 | 126 | * New ``bzr cp`` command which copies files (but does not currently track | 126 | * New ``bzr cp`` command which copies files (but does not currently track |
549 | 127 | history). (Jelmer Vernooij, start towards #269095) | 127 | history). (Jelmer Vernooij, start towards #269095) |
550 | 128 | 128 | ||
551 | 129 | * ``bzr push`` will now update the remote working tree when it pushes to | ||
552 | 130 | a smart server running Breezy 3.0 or later. (Jelmer Vernooij, #325355) | ||
553 | 131 | |||
554 | 129 | Bug Fixes | 132 | Bug Fixes |
555 | 130 | ********* | 133 | ********* |
556 | 131 | 134 |
All the plumbing for this is in place, but the trouble is that this requires breaking out of the chroot since WorkingTree requires a filesystem path.