Merge lp:~jelmer/bzr/switch-possible-transports into lp:bzr

Proposed by Jelmer Vernooij
Status: Superseded
Proposed branch: lp:~jelmer/bzr/switch-possible-transports
Merge into: lp:bzr
Prerequisite: lp:~jelmer/bzr/count-transport-connections
Diff against target: 405 lines (+87/-48) (has conflicts)
11 files modified
bzrlib/builtins.py (+33/-21)
bzrlib/bzrdir.py (+9/-8)
bzrlib/controldir.py (+2/-1)
bzrlib/plugins/weave_fmt/bzrdir.py (+2/-2)
bzrlib/plugins/weave_fmt/workingtree.py (+3/-2)
bzrlib/switch.py (+13/-6)
bzrlib/tests/blackbox/test_switch.py (+1/-1)
bzrlib/tests/test_workingtree.py (+2/-2)
bzrlib/workingtree_3.py (+1/-1)
bzrlib/workingtree_4.py (+6/-4)
doc/en/release-notes/bzr-2.5.txt (+15/-0)
Text conflict in doc/en/release-notes/bzr-2.5.txt
To merge this branch: bzr merge lp:~jelmer/bzr/switch-possible-transports
Reviewer Review Type Date Requested Status
Vincent Ladeuil Needs Fixing
Martin Packman Pending
Review via email: mp+86595@code.launchpad.net

This proposal supersedes a proposal from 2011-12-21.

This proposal has been superseded by a proposal from 2011-12-22.

Description of the change

Use possible_transports in 'bzr switch'.

This means that switching a lightweight checkout from one branch on Launchpad
to another branch on Launchpad no longer requires five connections, but just one.
This greatly speeds up switching.

Add a possible_transports argument to BzrDir.open_workingtree(). This is
necessary, as it opens branches, so it needs to pass possible_transports along
(e.g. in case the local branch is a branch reference).

Switch could still be improved to actually do fewer open_branch() calls,
but there's a separate bug about that - bug #812295

To post a comment you must log in.
Revision history for this message
Martin Packman (gz) wrote : Posted in a previous version of this proposal

This seems like a pretty reasonable interface change overall. Though not reopening the same branch should be the end goal, you'd still want this so that opening different remote branches over the same transport would work.

Does seem like some automated testing is needed. Even if we don't have infrastructure for tracking connection opens at the moment for the switch side, checking that open_workingtree passes the transport down correctly seems necessary.

review: Needs Fixing
Revision history for this message
Jelmer Vernooij (jelmer) wrote : Posted in a previous version of this proposal

I've now updated it to test the number of connections. You can nicely see the HPSS call count down from 38 down to 24, and the number of connections from 7 to 1. :-)

Revision history for this message
Vincent Ladeuil (vila) wrote : Posted in a previous version of this proposal

... profit ;)

28 + possible_transports.append(br_from.bzrdir.root_transport)
75 + possible_transports.append(branch.bzrdir.transport)
97 + possible_transports.append(to_branch.bzrdir.transport)

Huh ? This shouldn't be required, you already passed possible_transports
which, being a container, should have received the new transport (or reused
an existing one).

May be you added them before adding possible_transport as a parameter where
needed ? Having to do both has a weird smell otherwise...

I won't block on that but would like to understand what I'm missing here... (I would approve with both hands otherwise ;).

review: Needs Information
Revision history for this message
Jelmer Vernooij (jelmer) wrote : Posted in a previous version of this proposal

Am 21/12/11 11:36, schrieb Vincent Ladeuil:
> Review: Needs Information
>
> ... profit ;)
>
> 28 + possible_transports.append(br_from.bzrdir.root_transport)
> 75 + possible_transports.append(branch.bzrdir.transport)
> 97 + possible_transports.append(to_branch.bzrdir.transport)
>
> Huh ? This shouldn't be required, you already passed possible_transports
> which, being a container, should have received the new transport (or reused
> an existing one).
possible_transports is read-only as far as I know. It's always the
callers that update it, as far as I can tell. None of our code updates
possible_transports when it is passed AFAIK.

> May be you added them before adding possible_transport as a parameter where
> needed ? Having to do both has a weird smell otherwise...
>
> I won't block on that but would like to understand what I'm missing here... (I would approve with both hands otherwise ;).
:-) Thanks

Cheers,

Jelmer

Revision history for this message
Jelmer Vernooij (jelmer) wrote : Posted in a previous version of this proposal

You were right, bzrlib.transport already takes care of updating possible_transports.

I've updated the branch to not mess with possible_transports in bzrlib.builtins, and the tests still pass.

Revision history for this message
Vincent Ladeuil (vila) wrote : Posted in a previous version of this proposal

28 + possible_transports.append(br_from.bzrdir.root_transport)

Left over ?

138 + if possible_transports is None:
139 + possible_transports = []
140 + else:
141 + possible_transports = list(possible_transports) + [
142 + self.root_transport]

Are you sure you need that ? That seems very bad at it creates a new object which breaks the sharing with callers. You should just use .append() if you really need to, but do you ?

review: Needs Fixing
Revision history for this message
Jelmer Vernooij (jelmer) wrote : Posted in a previous version of this proposal

On 12/21/2011 06:37 PM, Vincent Ladeuil wrote:
> Review: Needs Fixing
>
> 28 + possible_transports.append(br_from.bzrdir.root_transport)
>
> Left over ?
Yep, removed now.
>
>
> 138 + if possible_transports is None:
> 139 + possible_transports = []
> 140 + else:
> 141 + possible_transports = list(possible_transports) + [
> 142 + self.root_transport]
>
> Are you sure you need that ? That seems very bad at it creates a new
object which breaks the sharing with callers. You should just use
.append() if you really need to, but do you ?
Apparently it's not necessary. Removed now as well.

Cheers,

Jelmer

Revision history for this message
Vincent Ladeuil (vila) wrote :

155 + if possible_transports is None:
156 + possible_transports = []
157 + else:
158 + possible_transports = list(possible_transports) + [
159 + self.root_transport]

Again ????

You should only need:

if possible_transports is None:
    possible_transports = []

review: Needs Fixing

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/builtins.py'
2--- bzrlib/builtins.py 2011-12-21 16:52:39 +0000
3+++ bzrlib/builtins.py 2011-12-21 18:08:32 +0000
4@@ -1342,7 +1342,9 @@
5 to_location = getattr(br_from, "name", None)
6 if to_location is None:
7 to_location = urlutils.derive_to_location(from_location)
8- to_transport = transport.get_transport(to_location)
9+ possible_transports = []
10+ to_transport = transport.get_transport(to_location,
11+ possible_transports=possible_transports)
12 try:
13 to_transport.mkdir('.')
14 except errors.FileExists:
15@@ -1371,15 +1373,14 @@
16 try:
17 # preserve whatever source format we have.
18 to_dir = br_from.bzrdir.sprout(to_transport.base, revision_id,
19- possible_transports=[to_transport],
20- accelerator_tree=accelerator_tree,
21- hardlink=hardlink, stacked=stacked,
22- force_new_repo=standalone,
23- create_tree_if_local=not no_tree,
24- source_branch=br_from)
25+ possible_transports=possible_transports,
26+ accelerator_tree=accelerator_tree,
27+ hardlink=hardlink, stacked=stacked,
28+ force_new_repo=standalone,
29+ create_tree_if_local=not no_tree,
30+ source_branch=br_from)
31 branch = to_dir.open_branch(
32- possible_transports=[
33- br_from.bzrdir.root_transport, to_transport])
34+ possible_transports=possible_transports)
35 except errors.NoSuchRevision:
36 to_transport.delete_tree('.')
37 msg = gettext("The branch {0} has no revision {1}.").format(
38@@ -1399,13 +1400,15 @@
39 note(ngettext('Branched %d revision.', 'Branched %d revisions.', branch.revno()) % branch.revno())
40 if bind:
41 # Bind to the parent
42- parent_branch = Branch.open(from_location)
43+ parent_branch = Branch.open(from_location,
44+ possible_transports=possible_transports)
45 branch.bind(parent_branch)
46 note(gettext('New branch bound to %s') % from_location)
47 if switch:
48 # Switch to the new branch
49 wt, _ = WorkingTree.open_containing('.')
50- _mod_switch.switch(wt.bzrdir, branch)
51+ _mod_switch.switch(wt.bzrdir, branch,
52+ possible_transports=possible_transports)
53 note(gettext('Switched to branch: %s'),
54 urlutils.unescape_for_display(branch.base, 'utf-8'))
55
56@@ -6109,14 +6112,17 @@
57 from bzrlib import switch
58 tree_location = directory
59 revision = _get_one_revision('switch', revision)
60- control_dir = controldir.ControlDir.open_containing(tree_location)[0]
61+ possible_transports = []
62+ control_dir = controldir.ControlDir.open_containing(
63+ tree_location, possible_transports=possible_transports)[0]
64 if to_location is None:
65 if revision is None:
66 raise errors.BzrCommandError(gettext('You must supply either a'
67 ' revision or a location'))
68 to_location = tree_location
69 try:
70- branch = control_dir.open_branch()
71+ branch = control_dir.open_branch(
72+ possible_transports=possible_transports)
73 had_explicit_nick = branch.get_config().has_explicit_nickname()
74 except errors.NotBranchError:
75 branch = None
76@@ -6133,7 +6139,7 @@
77 # Perhaps the target control dir supports colocated branches?
78 try:
79 root = controldir.ControlDir.open(this_url,
80- possible_transports=[control_dir.user_transport])
81+ possible_transports=possible_transports)
82 except errors.NotBranchError:
83 colocated = False
84 else:
85@@ -6145,25 +6151,31 @@
86 to_location = urlutils.join(
87 this_url, '..', urlutils.escape(to_location))
88 to_branch = branch.bzrdir.sprout(to_location,
89- possible_transports=[branch.bzrdir.root_transport],
90- source_branch=branch).open_branch()
91+ possible_transports=possible_transports,
92+ source_branch=branch).open_branch(
93+ possible_transports=possible_transports)
94 else:
95 # Perhaps it's a colocated branch?
96 try:
97- to_branch = control_dir.open_branch(to_location)
98+ to_branch = control_dir.open_branch(
99+ to_location, possible_transports=possible_transports)
100 except (errors.NotBranchError, errors.NoColocatedBranchSupport):
101 try:
102- to_branch = Branch.open(to_location)
103+ to_branch = Branch.open(to_location,
104+ possible_transports=possible_transports)
105 except errors.NotBranchError:
106 this_url = self._get_branch_location(control_dir)
107 to_branch = Branch.open(
108 urlutils.join(
109- this_url, '..', urlutils.escape(to_location)))
110+ this_url, '..', urlutils.escape(to_location)),
111+ possible_transports=possible_transports)
112 if revision is not None:
113 revision = revision.as_revision_id(to_branch)
114- switch.switch(control_dir, to_branch, force, revision_id=revision)
115+ switch.switch(control_dir, to_branch, force, revision_id=revision,
116+ possible_transports=possible_transports)
117 if had_explicit_nick:
118- branch = control_dir.open_branch() #get the new branch!
119+ #get the new branch!
120+ branch = control_dir.open_branch(possible_transports=possible_transports)
121 branch.nick = to_branch.nick
122 note(gettext('Switched to branch: %s'),
123 urlutils.unescape_for_display(to_branch.base, 'utf-8'))
124
125=== modified file 'bzrlib/bzrdir.py'
126--- bzrlib/bzrdir.py 2011-12-19 13:23:58 +0000
127+++ bzrlib/bzrdir.py 2011-12-21 18:08:32 +0000
128@@ -373,13 +373,8 @@
129 if revision_id is not None:
130 fetch_spec_factory.add_revision_ids([revision_id])
131 fetch_spec_factory.source_branch_stop_revision_id = revision_id
132- if possible_transports is None:
133- possible_transports = []
134- else:
135- possible_transports = list(possible_transports) + [
136- self.root_transport]
137 target_transport = _mod_transport.get_transport(url,
138- possible_transports)
139+ possible_transports=possible_transports)
140 target_transport.ensure_base()
141 cloning_format = self.cloning_metadir(stacked)
142 # Create/update the result branch
143@@ -974,13 +969,19 @@
144 return format.open(self, _found=True)
145
146 def open_workingtree(self, unsupported=False,
147- recommend_upgrade=True):
148+ recommend_upgrade=True, possible_transports=None):
149 """See BzrDir.open_workingtree."""
150 from bzrlib.workingtree import WorkingTreeFormatMetaDir
151 format = WorkingTreeFormatMetaDir.find_format(self)
152 format.check_support_status(unsupported, recommend_upgrade,
153 basedir=self.root_transport.base)
154- return format.open(self, _found=True)
155+ if possible_transports is None:
156+ possible_transports = []
157+ else:
158+ possible_transports = list(possible_transports) + [
159+ self.root_transport]
160+ return format.open(self, possible_transports=possible_transports,
161+ _found=True)
162
163 def _get_config(self):
164 return config.TransportConfig(self.transport, 'control.conf')
165
166=== modified file 'bzrlib/controldir.py'
167--- bzrlib/controldir.py 2011-12-19 13:23:58 +0000
168+++ bzrlib/controldir.py 2011-12-21 18:08:32 +0000
169@@ -263,7 +263,8 @@
170 raise NotImplementedError(self.find_repository)
171
172 def open_workingtree(self, _unsupported=False,
173- recommend_upgrade=True, from_branch=None):
174+ recommend_upgrade=True, from_branch=None,
175+ possible_transports=None):
176 """Open the workingtree object at this ControlDir if one is present.
177
178 :param recommend_upgrade: Optional keyword parameter, when True (the
179
180=== modified file 'bzrlib/plugins/weave_fmt/bzrdir.py'
181--- bzrlib/plugins/weave_fmt/bzrdir.py 2011-12-19 13:23:58 +0000
182+++ bzrlib/plugins/weave_fmt/bzrdir.py 2011-12-21 18:08:32 +0000
183@@ -966,14 +966,14 @@
184 from bzrlib.plugins.weave_fmt.repository import RepositoryFormat5
185 return RepositoryFormat5().open(self, _found=True)
186
187- def open_workingtree(self, _unsupported=False,
188+ def open_workingtree(self, possible_transports=None, _unsupported=False,
189 recommend_upgrade=True):
190 """See BzrDir.create_workingtree."""
191 from bzrlib.plugins.weave_fmt.workingtree import WorkingTreeFormat2
192 wt_format = WorkingTreeFormat2()
193 # we don't warn here about upgrades; that ought to be handled for the
194 # bzrdir as a whole
195- return wt_format.open(self, _found=True)
196+ return wt_format.open(self, possible_transports, _found=True)
197
198
199 class BzrDir6(BzrDirPreSplitOut):
200
201=== modified file 'bzrlib/plugins/weave_fmt/workingtree.py'
202--- bzrlib/plugins/weave_fmt/workingtree.py 2011-12-19 13:23:58 +0000
203+++ bzrlib/plugins/weave_fmt/workingtree.py 2011-12-21 18:08:32 +0000
204@@ -120,7 +120,7 @@
205 from bzrlib.plugins.weave_fmt.bzrdir import BzrDirFormat6
206 self._matchingbzrdir = BzrDirFormat6()
207
208- def open(self, a_bzrdir, _found=False):
209+ def open(self, a_bzrdir, possible_transports=None, _found=False):
210 """Return the WorkingTree object for a_bzrdir
211
212 _found is a private parameter, do not use it. It is used to indicate
213@@ -131,11 +131,12 @@
214 raise NotImplementedError
215 if not isinstance(a_bzrdir.transport, LocalTransport):
216 raise errors.NotLocalUrl(a_bzrdir.transport.base)
217+ branch = a_bzrdir.open_branch(possible_transports)
218 wt = WorkingTree2(a_bzrdir.root_transport.local_abspath('.'),
219 _internal=True,
220 _format=self,
221 _bzrdir=a_bzrdir,
222- _control_files=a_bzrdir.open_branch().control_files)
223+ _control_files=branch.control_files)
224 return wt
225
226
227
228=== modified file 'bzrlib/switch.py'
229--- bzrlib/switch.py 2011-12-18 15:28:38 +0000
230+++ bzrlib/switch.py 2011-12-21 18:08:32 +0000
231@@ -32,7 +32,9 @@
232 for hook in hooks:
233 hook(params)
234
235-def switch(control_dir, to_branch, force=False, quiet=False, revision_id=None):
236+
237+def switch(control_dir, to_branch, force=False, quiet=False, revision_id=None,
238+ possible_transports=None):
239 """Switch the branch associated with a checkout.
240
241 :param control_dir: ControlDir of the checkout to change
242@@ -40,23 +42,28 @@
243 :param force: skip the check for local commits in a heavy checkout
244 :param revision_id: revision ID to switch to.
245 """
246- _check_pending_merges(control_dir, force)
247+ _check_pending_merges(control_dir, possible_transports, force)
248 try:
249- source_repository = control_dir.open_branch().repository
250+ from_branch = control_dir.open_branch(
251+ possible_transports=possible_transports)
252+ source_repository = from_branch.repository
253 except errors.NotBranchError:
254 source_repository = to_branch.repository
255 _set_branch_location(control_dir, to_branch, force)
256- tree = control_dir.open_workingtree()
257+ tree = control_dir.open_workingtree(
258+ possible_transports=possible_transports)
259 _update(tree, source_repository, quiet, revision_id)
260 _run_post_switch_hooks(control_dir, to_branch, force, revision_id)
261
262-def _check_pending_merges(control, force=False):
263+
264+def _check_pending_merges(control, possible_transports=None, force=False):
265 """Check that there are no outstanding pending merges before switching.
266
267 :param control: ControlDir of the branch to check
268 """
269 try:
270- tree = control.open_workingtree()
271+ tree = control.open_workingtree(
272+ possible_transports=possible_transports)
273 except errors.NotBranchError, ex:
274 # Lightweight checkout and branch is no longer there
275 if force:
276
277=== modified file 'bzrlib/tests/blackbox/test_switch.py'
278--- bzrlib/tests/blackbox/test_switch.py 2011-12-21 18:08:30 +0000
279+++ bzrlib/tests/blackbox/test_switch.py 2011-12-21 18:08:32 +0000
280@@ -449,5 +449,5 @@
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(24, self.hpss_calls)
284- self.assertLength(5, self.hpss_connections)
285+ self.assertLength(1, self.hpss_connections)
286 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
287
288=== modified file 'bzrlib/tests/test_workingtree.py'
289--- bzrlib/tests/test_workingtree.py 2011-12-11 02:43:03 +0000
290+++ bzrlib/tests/test_workingtree.py 2011-12-21 18:08:32 +0000
291@@ -149,7 +149,7 @@
292 def is_supported(self):
293 return False
294
295- def open(self, transport, _found=False):
296+ def open(self, path, possible_transports=None, _found=False):
297 return "opened tree."
298
299
300@@ -169,7 +169,7 @@
301 def is_supported(self):
302 return False
303
304- def open(self, transport, _found=False):
305+ def open(self, path, possible_transports=None, _found=False):
306 raise NotImplementedError(self.open)
307
308
309
310=== modified file 'bzrlib/workingtree_3.py'
311--- bzrlib/workingtree_3.py 2011-12-19 13:23:58 +0000
312+++ bzrlib/workingtree_3.py 2011-12-21 18:08:32 +0000
313@@ -237,7 +237,7 @@
314 def _initial_inventory(self):
315 return inventory.Inventory()
316
317- def open(self, a_bzrdir, _found=False):
318+ def open(self, a_bzrdir, possible_transports=None, _found=False):
319 """Return the WorkingTree object for a_bzrdir
320
321 _found is a private parameter, do not use it. It is used to indicate
322
323=== modified file 'bzrlib/workingtree_4.py'
324--- bzrlib/workingtree_4.py 2011-12-19 17:39:35 +0000
325+++ bzrlib/workingtree_4.py 2011-12-21 18:08:32 +0000
326@@ -1560,7 +1560,7 @@
327 :param wt: the WorkingTree object
328 """
329
330- def open(self, a_bzrdir, _found=False):
331+ def open(self, a_bzrdir, possible_transports=None, _found=False):
332 """Return the WorkingTree object for a_bzrdir
333
334 _found is a private parameter, do not use it. It is used to indicate
335@@ -1571,17 +1571,19 @@
336 raise NotImplementedError
337 if not isinstance(a_bzrdir.transport, LocalTransport):
338 raise errors.NotLocalUrl(a_bzrdir.transport.base)
339- wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir))
340+ wt = self._open(a_bzrdir, self._open_control_files(a_bzrdir),
341+ possible_transports)
342 return wt
343
344- def _open(self, a_bzrdir, control_files):
345+ def _open(self, a_bzrdir, control_files, possible_transports=None):
346 """Open the tree itself.
347
348 :param a_bzrdir: the dir for the tree.
349 :param control_files: the control files for the tree.
350 """
351 return self._tree_class(a_bzrdir.root_transport.local_abspath('.'),
352- branch=a_bzrdir.open_branch(),
353+ branch=a_bzrdir.open_branch(
354+ possible_transports=possible_transports),
355 _format=self,
356 _bzrdir=a_bzrdir,
357 _control_files=control_files)
358
359=== modified file 'doc/en/release-notes/bzr-2.5.txt'
360--- doc/en/release-notes/bzr-2.5.txt 2011-12-21 17:42:34 +0000
361+++ doc/en/release-notes/bzr-2.5.txt 2011-12-21 18:08:32 +0000
362@@ -36,21 +36,33 @@
363
364 * New HPSS call for ``Repository.reconcile``. (Jelmer Vernooij, #894455)
365
366+<<<<<<< TREE
367 * Merge now has two new hooks ``pre_merge`` and ``post_merge``
368 that are called before and after a merge and can make
369 additional modifications to the trees involved.
370 (Jelmer Vernooij, #906877)
371
372+=======
373+* ``bzr send`` now only opens a single connection, rather than two,
374+ to the target branch. (Jelmer Vernooij)
375+
376+>>>>>>> MERGE-SOURCE
377 * Override the value returned by ``sys.getfilesystemencoding()`` for the bzr
378 script to utf-8 when it would otherwise be ascii on a posix system. This
379 will mean bzr works with non-ascii files when no locale or an incorrect
380 locale is set. (Martin Packman, #794353)
381
382+<<<<<<< TREE
383 * ``bzr branches`` now indicates the active colocated branch.
384 (Jelmer Vernooij, #891667)
385
386 * ``bzr send`` now only opens a single connection, rather than two,
387 to the target branch. (Jelmer Vernooij)
388+=======
389+* Switching a lightweight checkout to a new branch opens only a single
390+ external connection now per branch rather than 5.
391+ (Jelmer Vernooij)
392+>>>>>>> MERGE-SOURCE
393
394 Bug Fixes
395 *********
396@@ -95,6 +107,9 @@
397 .. Changes that may require updates in plugins or other code that uses
398 bzrlib.
399
400+* ``BzrDir.open_workingtree`` and ``WorkingTreeFormat.open`` now take an optional
401+ ``possible_transports`` argument. (Jelmer Vernooij)
402+
403 * ``Config.signature_needed``, ``Config.signing_policy``,
404 ``Config.gpg_signing_key``, ``Config.gpg_signing_command``,
405 ``Config.checking_policy`` and ``Config.post_commit`` are now deprecated.