Merge lp:~jelmer/brz/hpss-working-tree-update into lp:brz

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
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.
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

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.

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 RemoteWorkingTree.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
=== modified file 'breezy/bzr/bzrdir.py'
--- breezy/bzr/bzrdir.py 2018-05-07 14:35:05 +0000
+++ breezy/bzr/bzrdir.py 2018-05-19 02:48:08 +0000
@@ -1103,13 +1103,13 @@
1103 return format.open(self, _found=True)1103 return format.open(self, _found=True)
11041104
1105 def open_workingtree(self, unsupported=False,1105 def open_workingtree(self, unsupported=False,
1106 recommend_upgrade=True):1106 recommend_upgrade=True, remote=False):
1107 """See BzrDir.open_workingtree."""1107 """See BzrDir.open_workingtree."""
1108 from .workingtree import WorkingTreeFormatMetaDir1108 from .workingtree import WorkingTreeFormatMetaDir
1109 format = WorkingTreeFormatMetaDir.find_format(self)1109 format = WorkingTreeFormatMetaDir.find_format(self)
1110 format.check_support_status(unsupported, recommend_upgrade,1110 format.check_support_status(unsupported, recommend_upgrade,
1111 basedir=self.root_transport.base)1111 basedir=self.root_transport.base)
1112 return format.open(self, _found=True)1112 return format.open(self, remote=remote, _found=True)
11131113
1114 def _get_config(self):1114 def _get_config(self):
1115 return config.TransportConfig(self.transport, 'control.conf')1115 return config.TransportConfig(self.transport, 'control.conf')
11161116
=== modified file 'breezy/bzr/remote.py'
--- breezy/bzr/remote.py 2018-05-13 16:23:17 +0000
+++ breezy/bzr/remote.py 2018-05-19 02:48:08 +0000
@@ -45,6 +45,7 @@
45 inventory_delta,45 inventory_delta,
46 vf_repository,46 vf_repository,
47 vf_search,47 vf_search,
48 workingtree,
48 )49 )
49from .branch import BranchReferenceFormat50from .branch import BranchReferenceFormat
50from ..branch import BranchWriteLockResult51from ..branch import BranchWriteLockResult
@@ -883,9 +884,12 @@
883 self._has_working_tree = (response[0] == b'yes')884 self._has_working_tree = (response[0] == b'yes')
884 return self._has_working_tree885 return self._has_working_tree
885886
886 def open_workingtree(self, recommend_upgrade=True):887 def open_workingtree(self, recommend_upgrade=True, remote=False):
887 if self.has_workingtree():888 if self.has_workingtree():
888 raise errors.NotLocalUrl(self.root_transport)889 if remote:
890 return RemoteWorkingTree(self, self.open_branch())
891 else:
892 raise errors.NotLocalUrl(self.root_transport.base)
889 else:893 else:
890 raise errors.NoWorkingTree(self.root_transport.base)894 raise errors.NoWorkingTree(self.root_transport.base)
891895
@@ -4127,6 +4131,168 @@
4127 return self._bzrdir._real_bzrdir4131 return self._bzrdir._real_bzrdir
41284132
41294133
4134class RemoteWorkingTreeFormat(workingtree.WorkingTreeFormat):
4135
4136 def __init__(self):
4137 super(RemoteWorkingTreeFormat, self).__init__()
4138
4139 def get_format_description(self):
4140 return "Remote Working Tree"
4141
4142 def __eq__(self, other):
4143 return (isinstance(other, RemoteWorkingTreeFormat) and
4144 self.__dict__ == other.__dict__)
4145
4146
4147class RemoteWorkingTree(workingtree.WorkingTree, _RpcHelper, lock._RelockDebugMixin):
4148 """Working tree stored on a server accessed by HPSS RPC.
4149
4150 At the moment this only implements .update(). All other methods
4151 raise _ensure_real().
4152 """
4153
4154 def __init__(self, remote_controldir, remote_branch, _client=None):
4155 self.controldir = remote_controldir
4156 self._branch = remote_branch
4157 self._format = RemoteWorkingTreeFormat()
4158 self._real_workingtree = None
4159 self._lock_mode = None
4160 self._lock_count = 0
4161 if _client is None:
4162 self._client = remote_controldir._client
4163 else:
4164 self._client = _client
4165
4166 @property
4167 def user_transport(self):
4168 return self.controldir.user_transport
4169
4170 @property
4171 def control_transport(self):
4172 # XXX: Normally you shouldn't directly get at the remote repository
4173 # transport, but I'm not sure it's worth making this method
4174 # optional -- mbp 2010-04-21
4175 return self.controldir.get_repository_transport(None)
4176
4177 def _translate_error(self, err, **context):
4178 _translate_error(err, workingtree=self, **context)
4179
4180 def _ensure_real(self):
4181 # Most working tree operations require local access at the moment.
4182 # When Bazaar working trees can be accessed over
4183 # transports, set self._real_workingtree here.
4184 raise errors.NotLocalUrl(self.controldir.root_transport)
4185
4186 def is_locked(self):
4187 return self._lock_count >= 1
4188
4189 def lock_read(self):
4190 """Lock the working tree for read operations.
4191
4192 :return: A bzrlib.lock.LogicalLockResult.
4193 """
4194 # wrong eventually - want a local lock cache context
4195 if not self._lock_mode:
4196 self._note_lock('r')
4197 self._lock_mode = 'r'
4198 self._lock_count = 1
4199 if self._real_workingtree is not None:
4200 self._real_workingtree.lock_read()
4201 self.branch.lock_read()
4202 else:
4203 self._lock_count += 1
4204 return lock.LogicalLockResult(self.unlock)
4205
4206 def lock_write(self):
4207 if not self._lock_mode:
4208 self._note_lock('w')
4209 if self._real_workingtree is not None:
4210 self._real_workingtree.lock_write()
4211 self._lock_mode = 'w'
4212 self._lock_count = 1
4213 self.branch.lock_write()
4214 elif self._lock_mode == 'r':
4215 raise errors.ReadOnlyError(self)
4216 else:
4217 self._lock_count += 1
4218 return lock.LogicalLockResult(
4219 lambda: self.unlock())
4220
4221 @only_raises(errors.LockNotHeld, errors.LockBroken)
4222 def unlock(self):
4223 if not self._lock_count:
4224 return lock.cant_unlock_not_held(self)
4225 self._lock_count -= 1
4226 if self._lock_count > 0:
4227 return
4228 old_mode = self._lock_mode
4229 self._lock_mode = None
4230 try:
4231 if self._real_workingtree is not None:
4232 self._real_workingtree.unlock()
4233 finally:
4234 self.branch.unlock()
4235
4236 def get_physical_lock_status(self):
4237 """See WorkingTree.get_physical_lock_status()."""
4238 path = self.controldir._path_for_remote_call(self._client)
4239 try:
4240 response = self._call(b'WorkingTree.get_physical_lock_status', path)
4241 except errors.UnknownSmartMethod:
4242 self._ensure_real()
4243 return self._real_workingtree.get_physical_lock_status()
4244 if response[0] not in (b'yes', b'no'):
4245 raise errors.UnexpectedSmartServerResponse(response)
4246 return (response[0] == b'yes')
4247
4248 def _update_rpc(self, old_tip=None, change_reporter=None,
4249 possible_transports=None, revision=None, show_base=False):
4250 medium = self.controldir._client._medium
4251 path = self.controldir._path_for_remote_call(self.controldir._client)
4252 if revision is None:
4253 revision = ''
4254 if old_tip is None:
4255 old_tip = ''
4256 response, response_handler = self._call_expecting_body(
4257 'WorkingTree.update', path, revision, old_tip, show_base)
4258 if response[0] != 'ok':
4259 raise errors.UnexpectedSmartServerResponse(response)
4260 if change_reporter is None:
4261 response_handler.cancel_read_body()
4262 else:
4263 byte_stream = response_handler.read_streamed_body()
4264 data = ""
4265 for bytes in byte_stream:
4266 data += bytes
4267 lines = bytes.split("\n")
4268 data = lines.pop()
4269 for line in lines:
4270 if not line.startswith("change: "):
4271 raise errors.UnexpectedSmartServerResponse(response)
4272 change = bencode.bdecode_as_tuple(line[8:])
4273 # FIXME: More post-processing
4274 change_reporter.report(*change)
4275 return int(response[1])
4276
4277 def update(self, old_tip=None, change_reporter=None,
4278 possible_transports=None, revision=None, show_base=False):
4279 try:
4280 return self._update_rpc(old_tip=old_tip,
4281 change_reporter=change_reporter,
4282 possible_transports=possible_transports, revision=revision,
4283 show_base=show_base)
4284 except errors.UnknownSmartMethod:
4285 self._ensure_real()
4286 return self._real_workingtree.update(old_tip=old_tip,
4287 change_reporter=change_reporter,
4288 possible_transports=possible_transports,
4289 revision=revision, show_base=show_base)
4290
4291 def get_parent_ids(self):
4292 self._ensure_real()
4293 return self._real_workingtree.get_parent_ids()
4294
4295
4130error_translators = registry.Registry()4296error_translators = registry.Registry()
4131no_context_error_translators = registry.Registry()4297no_context_error_translators = registry.Registry()
41324298
41334299
=== modified file 'breezy/bzr/smart/branch.py'
--- breezy/bzr/smart/branch.py 2018-05-13 02:18:13 +0000
+++ breezy/bzr/smart/branch.py 2018-05-19 02:48:08 +0000
@@ -14,7 +14,7 @@
14# along with this program; if not, write to the Free Software14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1616
17"""Server-side branch related request implmentations."""17"""Server-side branch related request implementations."""
1818
19from __future__ import absolute_import19from __future__ import absolute_import
2020
2121
=== modified file 'breezy/bzr/smart/request.py'
--- breezy/bzr/smart/request.py 2018-05-13 02:18:13 +0000
+++ breezy/bzr/smart/request.py 2018-05-19 02:48:08 +0000
@@ -791,3 +791,9 @@
791request_handlers.register_lazy(791request_handlers.register_lazy(
792 b'Transport.is_readonly', 'breezy.bzr.smart.request',792 b'Transport.is_readonly', 'breezy.bzr.smart.request',
793 'SmartServerIsReadonly', info='read')793 'SmartServerIsReadonly', info='read')
794request_handlers.register_lazy(
795 b'WorkingTree.update', 'breezy.bzr.smart.workingtree',
796 'SmartServerWorkingTreeUpdate', info='mutate')
797request_handlers.register_lazy(
798 b'WorkingTree.get_physical_lock_status', 'breezy.bzr.smart.workingtree',
799 'SmartServerWorkingTreeGetPhysicalLockStatus', info='read')
794800
=== added file 'breezy/bzr/smart/workingtree.py'
--- breezy/bzr/smart/workingtree.py 1970-01-01 00:00:00 +0000
+++ breezy/bzr/smart/workingtree.py 2018-05-19 02:48:08 +0000
@@ -0,0 +1,115 @@
1# Copyright (C) 2011 Canonical Ltd
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2 of the License, or
6# (at your option) any later version.
7#
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12#
13# You should have received a copy of the GNU General Public License
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
16
17"""Server-side working tree related request implementations."""
18
19from __future__ import absolute_import
20
21
22from breezy import (
23 bencode,
24 delta as _mod_delta,
25 errors,
26 revision as _mod_revision,
27 )
28from breezy.controldir import ControlDir
29from breezy.bzr.smart.request import (
30 FailedSmartServerResponse,
31 SmartServerRequest,
32 SuccessfulSmartServerResponse,
33 )
34
35
36class SmartServerWorkingTreeRequest(SmartServerRequest):
37 """Base class for handling common working tree request logic.
38 """
39
40 def do(self, path, *args):
41 """Execute a request for a working tree at path.
42
43 All working tree requests take a path to the tree as their first
44 argument.
45
46 :param path: The path for the repository as received from the
47 client.
48 :return: A SmartServerResponse from self.do_with_workingtree().
49 """
50 transport = self.transport_from_client_path(path)
51 controldir = ControlDir.open_from_transport(transport)
52 workingtree = controldir.open_workingtree(remote=True)
53 return self.do_with_workingtree(workingtree, *args)
54
55 def do_with_workingtree(self, workingtree, *args):
56 raise NotImplementedError(self.do_with_workingtree)
57
58
59class SerializingChangeReporter(_mod_delta._ChangeReporter):
60 """Change reporter that serializes."""
61
62 def __init__(self):
63 self.chunks = []
64
65 def _format_path(self, path):
66 if path is None:
67 return ""
68 elif path == "":
69 return "."
70 else:
71 return path.encode("utf-8")
72
73 def _format_kind(self, kind):
74 if kind is None:
75 return ""
76 return kind
77
78 def report(self, file_id, paths, versioned, renamed, modified, exe_change,
79 kind):
80 self.chunks.append("report: %s\n" % bencode.bencode((file_id,
81 self._format_path(paths[0]), self._format_path(paths[1]),
82 versioned, renamed, modified, exe_change,
83 self._format_kind(kind[0]), self._format_kind(kind[1]))))
84
85
86class SmartServerWorkingTreeUpdate(SmartServerWorkingTreeRequest):
87 """Update a working tree.
88
89 New in 3.0.
90 """
91
92 def do_with_workingtree(self, workingtree, revision, old_tip, show_base):
93 if revision == '':
94 revision = None
95 if old_tip == '':
96 old_tip = None
97 reporter = SerializingChangeReporter()
98 with workingtree.lock_write(): # TODO(jelmer): Accept token
99 nconflicts = workingtree.update(change_reporter=reporter,
100 revision=revision, old_tip=old_tip, show_base=show_base)
101 return SuccessfulSmartServerResponse(('ok', nconflicts),
102 body_stream=iter(reporter.chunks))
103
104
105class SmartServerWorkingTreeGetPhysicalLockStatus(SmartServerWorkingTreeRequest):
106 """Get the physical lock status for a working tree.
107
108 New in 3.0.
109 """
110
111 def do_with_workingtree(self, workingtree):
112 if workingtree.get_physical_lock_status():
113 return SuccessfulSmartServerResponse((b'yes', ))
114 else:
115 return SuccessfulSmartServerResponse((b'no', ))
0116
=== modified file 'breezy/bzr/workingtree_4.py'
--- breezy/bzr/workingtree_4.py 2018-03-28 01:17:12 +0000
+++ breezy/bzr/workingtree_4.py 2018-05-19 02:48:08 +0000
@@ -1567,7 +1567,7 @@
1567 :param wt: the WorkingTree object1567 :param wt: the WorkingTree object
1568 """1568 """
15691569
1570 def open(self, a_controldir, _found=False):1570 def open(self, a_controldir, remote=False, _found=False):
1571 """Return the WorkingTree object for a_controldir1571 """Return the WorkingTree object for a_controldir
15721572
1573 _found is a private parameter, do not use it. It is used to indicate1573 _found is a private parameter, do not use it. It is used to indicate
@@ -1576,7 +1576,7 @@
1576 if not _found:1576 if not _found:
1577 # we are being called directly and must probe.1577 # we are being called directly and must probe.
1578 raise NotImplementedError1578 raise NotImplementedError
1579 if not isinstance(a_controldir.transport, LocalTransport):1579 if not remote and not isinstance(a_controldir.transport, LocalTransport):
1580 raise errors.NotLocalUrl(a_controldir.transport.base)1580 raise errors.NotLocalUrl(a_controldir.transport.base)
1581 wt = self._open(a_controldir, self._open_control_files(a_controldir))1581 wt = self._open(a_controldir, self._open_control_files(a_controldir))
1582 return wt1582 return wt
15831583
=== modified file 'breezy/controldir.py'
--- breezy/controldir.py 2018-05-07 14:35:05 +0000
+++ breezy/controldir.py 2018-05-19 02:48:08 +0000
@@ -306,6 +306,7 @@
306 (but still fully supported).306 (but still fully supported).
307 :param from_branch: override controldir branch (for lightweight307 :param from_branch: override controldir branch (for lightweight
308 checkouts)308 checkouts)
309 :param remote: open working tree even if it's remote
309 """310 """
310 raise NotImplementedError(self.open_workingtree)311 raise NotImplementedError(self.open_workingtree)
311312
@@ -444,9 +445,9 @@
444 # FIXME: Should be done only if we succeed ? -- vila 2012-01-18445 # FIXME: Should be done only if we succeed ? -- vila 2012-01-18
445 source.set_push_location(br_to.base)446 source.set_push_location(br_to.base)
446 try:447 try:
447 tree_to = self.open_workingtree()448 tree_to = self.open_workingtree(remote=True)
448 except errors.NotLocalUrl:449 except errors.NotLocalUrl:
449 push_result.branch_push_result = source.push(br_to, 450 push_result.branch_push_result = source.push(br_to,
450 overwrite, stop_revision=revision_id, lossy=lossy)451 overwrite, stop_revision=revision_id, lossy=lossy)
451 push_result.workingtree_updated = False452 push_result.workingtree_updated = False
452 except errors.NoWorkingTree:453 except errors.NoWorkingTree:
453454
=== modified file 'breezy/tests/blackbox/test_push.py'
--- breezy/tests/blackbox/test_push.py 2018-03-26 00:54:10 +0000
+++ breezy/tests/blackbox/test_push.py 2018-05-19 02:48:08 +0000
@@ -266,6 +266,23 @@
266 # ancestry266 # ancestry
267 self.assertEqual([('A',), ('C',)], sorted(target_repo.revisions.keys()))267 self.assertEqual([('A',), ('C',)], sorted(target_repo.revisions.keys()))
268268
269 def test_push_smart_updates_tree(self):
270 self.setup_smart_server_with_call_log()
271 t = self.make_branch_and_tree('from')
272 self.build_tree(['from/afile'])
273 t.add(['afile'])
274 self.make_branch_and_tree('to')
275 t.commit(allow_pointless=True, message='first commit')
276 self.reset_smart_call_log()
277 self.run_bzr(['push', self.get_url('to')], working_dir='from')
278 # This figure represent the amount of work to perform this use case. It
279 # is entirely ok to reduce this number if a test fails due to rpc_count
280 # being too low. If rpc_count increases, more network roundtrips have
281 # become necessary for this use case. Please do not adjust this number
282 # upwards without agreement from bzr's network support maintainers.
283 self.assertLength(11, self.hpss_calls)
284 self.assertPathExists('to/afile')
285
269 def test_push_smart_non_stacked_streaming_acceptance(self):286 def test_push_smart_non_stacked_streaming_acceptance(self):
270 self.setup_smart_server_with_call_log()287 self.setup_smart_server_with_call_log()
271 t = self.make_branch_and_tree('from')288 t = self.make_branch_and_tree('from')
272289
=== modified file 'breezy/tests/test_remote.py'
--- breezy/tests/test_remote.py 2018-05-13 02:45:58 +0000
+++ breezy/tests/test_remote.py 2018-05-19 02:48:08 +0000
@@ -60,6 +60,7 @@
60 RemoteBranchFormat,60 RemoteBranchFormat,
61 RemoteBzrDir,61 RemoteBzrDir,
62 RemoteBzrDirFormat,62 RemoteBzrDirFormat,
63 RemoteWorkingTree,
63 RemoteRepository,64 RemoteRepository,
64 RemoteRepositoryFormat,65 RemoteRepositoryFormat,
65 )66 )
@@ -659,11 +660,24 @@
659 client, transport = self.make_fake_client_and_transport()660 client, transport = self.make_fake_client_and_transport()
660 client.add_expected_call(661 client.add_expected_call(
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'))
663 branch_network_name = self.get_branch_format().network_name()
664 repo_network_name = self.get_repo_format().network_name()
665 client.add_expected_call(
666 'BzrDir.open_branchV3', ('quack/',),
667 'success', ('branch', branch_network_name))
668 client.add_expected_call(
669 'BzrDir.find_repositoryV3', ('quack/',),
670 'success', ('ok', '', 'no', 'no', 'no', repo_network_name))
671 client.add_expected_call(
672 'Branch.get_stacked_on_url', ('quack/',),
673 'error', ('NotStacked',))
662 bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),674 bd = RemoteBzrDir(transport, RemoteBzrDirFormat(),
663 _client=client, _force_probe=True)675 _client=client, _force_probe=True)
664 self.assertIsInstance(bd, RemoteBzrDir)676 self.assertIsInstance(bd, RemoteBzrDir)
665 self.assertTrue(bd.has_workingtree())677 self.assertTrue(bd.has_workingtree())
666 self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)678 self.assertRaises(errors.NotLocalUrl, bd.open_workingtree)
679 tree = bd.open_workingtree(remote=True)
680 self.assertIsInstance(tree, RemoteWorkingTree)
667 self.assertFinished(client)681 self.assertFinished(client)
668682
669 def test_backwards_compat(self):683 def test_backwards_compat(self):
@@ -4261,6 +4275,46 @@
4261 repo.pack([b'hinta', b'hintb'])4275 repo.pack([b'hinta', b'hintb'])
42624276
42634277
4278class RemoteWorkingTreeTestCase(RemoteBranchTestCase):
4279
4280 def make_remote_working_tree(self, transport, client):
4281 """Make a RemoteWorkingTree using 'client' as its _SmartClient.
4282 """
4283 bzrdir = self.make_remote_bzrdir(transport, client)
4284 return RemoteWorkingTree(bzrdir, bzrdir.open_branch())
4285
4286
4287class TestWorkingTreeLocking(RemoteWorkingTreeTestCase):
4288
4289 def test_lock(self):
4290 transport = MemoryTransport()
4291 client = FakeClient(transport.base)
4292 branch_network_name = self.get_branch_format().network_name()
4293 repo_network_name = self.get_repo_format().network_name()
4294 client.add_expected_call(
4295 'BzrDir.open_branchV3', ('.',),
4296 'success', ('branch', branch_network_name))
4297 client.add_expected_call(
4298 'BzrDir.find_repositoryV3', ('.',),
4299 'success', ('ok', '', 'no', 'no', 'no', repo_network_name))
4300 client.add_expected_call(
4301 'Branch.get_stacked_on_url', ('.',),
4302 'error', ('NotStacked',))
4303 client.add_expected_call(
4304 'Branch.lock_write', ('.', '', ''),
4305 'success', ('ok', 'repo token', 'branch token'))
4306 client.add_expected_call(
4307 'Branch.unlock', ('.', 'repo token', 'branch token'),
4308 'success', ('ok', ))
4309 tree = self.make_remote_working_tree(transport, client)
4310 self.assertFalse(tree.is_locked())
4311 tree.lock_write()
4312 self.assertTrue(tree.is_locked())
4313 tree.unlock()
4314 self.assertFalse(tree.is_locked())
4315 self.assertFinished(client)
4316
4317
4264class TestRepositoryIterInventories(TestRemoteRepository):4318class TestRepositoryIterInventories(TestRemoteRepository):
4265 """Test Repository.iter_inventories."""4319 """Test Repository.iter_inventories."""
42664320
42674321
=== modified file 'breezy/workingtree.py'
--- breezy/workingtree.py 2018-03-25 00:39:16 +0000
+++ breezy/workingtree.py 2018-05-19 02:48:08 +0000
@@ -406,7 +406,7 @@
406 else:406 else:
407 parents = [last_rev]407 parents = [last_rev]
408 try:408 try:
409 merges_bytes = self._transport.get_bytes('pending-merges')409 merges_bytes = self.control_transport.get_bytes('pending-merges')
410 except errors.NoSuchFile:410 except errors.NoSuchFile:
411 pass411 pass
412 else:412 else:
@@ -584,7 +584,7 @@
584584
585 def _set_merges_from_parent_ids(self, parent_ids):585 def _set_merges_from_parent_ids(self, parent_ids):
586 merges = parent_ids[1:]586 merges = parent_ids[1:]
587 self._transport.put_bytes('pending-merges', b'\n'.join(merges),587 self.control_transport.put_bytes('pending-merges', b'\n'.join(merges),
588 mode=self.controldir._get_file_mode())588 mode=self.controldir._get_file_mode())
589589
590 def _filter_parent_ids_by_ancestry(self, revision_ids):590 def _filter_parent_ids_by_ancestry(self, revision_ids):
591591
=== modified file 'doc/en/release-notes/brz-3.0.txt'
--- doc/en/release-notes/brz-3.0.txt 2018-03-24 03:03:03 +0000
+++ doc/en/release-notes/brz-3.0.txt 2018-05-19 02:48:08 +0000
@@ -126,6 +126,9 @@
126 * New ``bzr cp`` command which copies files (but does not currently track126 * New ``bzr cp`` command which copies files (but does not currently track
127 history). (Jelmer Vernooij, start towards #269095)127 history). (Jelmer Vernooij, start towards #269095)
128128
129* ``bzr push`` will now update the remote working tree when it pushes to
130 a smart server running Breezy 3.0 or later. (Jelmer Vernooij, #325355)
131
129Bug Fixes132Bug Fixes
130*********133*********
131134

Subscribers

People subscribed via source and target branches