Merge lp:~spiv/bzr/sprout-does-not-reopen-repo into lp:bzr
- sprout-does-not-reopen-repo
- Merge into bzr.dev
Status: | Merged |
---|---|
Approved by: | Vincent Ladeuil |
Approved revision: | no longer in the source branch. |
Merged at revision: | 5570 |
Proposed branch: | lp:~spiv/bzr/sprout-does-not-reopen-repo |
Merge into: | lp:bzr |
Diff against target: |
550 lines (+133/-47) 10 files modified
bzrlib/branch.py (+45/-22) bzrlib/bzrdir.py (+7/-3) bzrlib/controldir.py (+26/-5) bzrlib/remote.py (+27/-11) bzrlib/tests/blackbox/test_branch.py (+1/-1) bzrlib/tests/test_branch.py (+1/-1) bzrlib/tests/test_bzrdir.py (+7/-0) bzrlib/tests/test_foreign.py (+4/-2) doc/en/release-notes/bzr-2.3.txt (+12/-0) doc/en/whats-new/whats-new-in-2.3.txt (+3/-2) |
To merge this branch: | bzr merge lp:~spiv/bzr/sprout-does-not-reopen-repo |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Vincent Ladeuil | Approve | ||
John A Meinel | Needs Information | ||
Review via email: mp+41037@code.launchpad.net |
Commit message
Avoid reopening (and relocking) the same branches/
Description of the change
This is a step towards bug 309682. It's not quite in a finished state, but I would appreciate feedback.
The idea here is pretty simple: ControlDir.sprout shouldn't open the same repository multiple times. This patch achieves that, and sure enough it causes the worst HPSS ratchet to improve by another notch.
There are a couple of dissatisfying aspects, though:
1. sprout calls branch.sprout after opening the result repository. So this
patch adds a new param (repository/
Branch.
signatures. Perhaps something more radical is called for, like a single
API to acquire or create both a branch and a repo?
2. For some reason, when stacking, the same fallback is added twice, which
confuses _unstack. (Maybe this only happens for RemoteBranches?) I'm
not sure exactly why this happens, but the hack to simply ignore
duplicate _activate_
is an XXX in the patch.
3. It's at least theoretically possible that the repository object we've
already acquired locally doesn't have the same location as the one
suggested by the server (e.g. if the server has a bug, or there's a race
with another client changing something, or perhaps it can even happen
legitimately?). What ought to happen in this case? I suppose this is
already an issue, which the code currently ignores because of the
independent open_repository calls. This is an XXX and AssertionError
in the patch.
Comments on these or any other parts of this patch are very welcome.
Vincent Ladeuil (vila) wrote : | # |
I would almost agree with John about the practical aspect (it works, let's land).
On the other hand, you have already spent a good amount of time on this which will be necessary again if we want to provide a cleaner solution.
But...
267 registry,
268 + repository,
269 revision as _mod_revision,
seems weird. I'd like jelmer's feedback here, but I'm pretty sure we pull controldir out of bzrdir to make sure it was isolated from bzr specifics and having to reintroduce repository here sounds like a step backward.
Also,
10 + # This fallback is already configured. This probably only
11 + # happens because BzrDir.sprout is a horrible mess. To avoid
12 + # confusing _unstack we don't add this a second time.
13 + # XXX: log a warning, maybe?
14 + return
is a sign that you're close to nailing the issue and I think you should ;)
On the overall, we have a 'repository' attribute on the branch object, so having to provide it should only occur when we are creating this object. Do we have that many different ways to create branches that you end up modifying so many signatures ? Doesn't it reveal a deeper issue ?
Andrew Bennetts (spiv) wrote : | # |
Vincent Ladeuil wrote:
> Review: Needs Information
> I would almost agree with John about the practical aspect (it works, let's land).
>
> On the other hand, you have already spent a good amount of time on this which will be necessary again if we want to provide a cleaner solution.
>
> But...
>
> 267 registry,
> 268 + repository,
> 269 revision as _mod_revision,
>
> seems weird. I'd like jelmer's feedback here, but I'm pretty sure we pull
> controldir out of bzrdir to make sure it was isolated from bzr specifics and
> having to reintroduce repository here sounds like a step backward.
That seems to be a leftover from an earlier version of this patch. It's
not actually used. I've removed that import (and gardened a couple of
others noticed by pyflakes). Thanks for spotting that.
> Also,
>
> 10 + # This fallback is already configured. This probably only
> 11 + # happens because BzrDir.sprout is a horrible mess. To avoid
> 12 + # confusing _unstack we don't add this a second time.
> 13 + # XXX: log a warning, maybe?
> 14 + return
>
> is a sign that you're close to nailing the issue and I think you should ;)
If I thought I was close I'd have nailed it already!
> On the overall, we have a 'repository' attribute on the branch object, so
> having to provide it should only occur when we are creating this object. Do we
> have that many different ways to create branches that you end up modifying so
> many signatures ? Doesn't it reveal a deeper issue ?
I agree, I think there's a deeper issue here, as my cover letter tried
to say. I'm not sure what a good way solution is.
Regarding the number of ways to create branches, here's one way to count
that, based on this diff:
* 8 definitions of BranchFormat.
* 4 definitions of ControlDir.
* 1 definition of Branch.sprout
That doesn't count definitions in test code.
Plus there's open methods, which create branch objects, which are also
affected:
* 3 definitions of BranchFormat.open
So yes, we do have 16 different ways to create a branch object, hence
why I touch so many methods. I don't see an obvious solution to that.
A possible approach might be to have the low-level
BranchFormat.
without the repository object being set, so returning only partially
initialized Branch objects. Then have higher-level APIs (along the
lines of the existing open_containg_
here too?) that assemble these into fully initialized objects with
.repository attributes filled in. There's the related consideration
that parts of the code want to open and/or create some combination of
branch/repo/tree in one logical step (and ideally one network
roundtrip), but the current design opens each of the bzrdir, repository,
and branch separately. I think this is part of the cause of the double
add_fallback ugliness that's an XXX for that matter.
So my suspicion is a fairly fundamental redesign of how we open/create
these things would be help a lot... but getting it right may take some
effort. And it's likely to be way more disruptive to bzr-svn and
friends than a new keyword param! Until someone has a concrete ...
Jelmer Vernooij (jelmer) wrote : | # |
I've only reviewed this for possible issues with foreign VCS implementations, and it seems fine in that regard (bearing in mind the new repository= parameter).
Vincent Ladeuil (vila) wrote : | # |
Thanks, Jelmer !
Ok, so my main concerns are addressed, just a few remarks, possibly tweaks:
379 + if not isinstance(
380 + raise AssertionError('xxx %r' % (repository,))
replacing this 'xxx' with something meaningful can only help people confronted with the raised assertion ;)
406 + raise AssertionError(
407 + 'diff %r: %r vs. (%r + %r)' %
408 + (url_diff, repository.
ditto (less sure here, the tb may be enough)
And finally, you summary in your reply above would be nice to keep in devnotes IMHO. Let's not redo this the next time we want to address the deeper issue.
Andrew Bennetts (spiv) wrote : | # |
sent to pqm by email
Preview Diff
1 | === modified file 'bzrlib/branch.py' |
2 | --- bzrlib/branch.py 2010-11-18 00:22:24 +0000 |
3 | +++ bzrlib/branch.py 2010-12-15 00:41:26 +0000 |
4 | @@ -105,6 +105,13 @@ |
5 | |
6 | def _activate_fallback_location(self, url): |
7 | """Activate the branch/repository from url as a fallback repository.""" |
8 | + for existing_fallback_repo in self.repository._fallback_repositories: |
9 | + if existing_fallback_repo.user_url == url: |
10 | + # This fallback is already configured. This probably only |
11 | + # happens because BzrDir.sprout is a horrible mess. To avoid |
12 | + # confusing _unstack we don't add this a second time. |
13 | + mutter('duplicate activation of fallback %r on %r', url, self) |
14 | + return |
15 | repo = self._get_fallback_repository(url) |
16 | if repo.has_same_location(self.repository): |
17 | raise errors.UnstackableLocationError(self.user_url, url) |
18 | @@ -809,7 +816,8 @@ |
19 | old_repository = self.repository |
20 | if len(old_repository._fallback_repositories) != 1: |
21 | raise AssertionError("can't cope with fallback repositories " |
22 | - "of %r" % (self.repository,)) |
23 | + "of %r (fallbacks: %r)" % (old_repository, |
24 | + old_repository._fallback_repositories)) |
25 | # Open the new repository object. |
26 | # Repositories don't offer an interface to remove fallback |
27 | # repositories today; take the conceptually simpler option and just |
28 | @@ -1266,7 +1274,8 @@ |
29 | return result |
30 | |
31 | @needs_read_lock |
32 | - def sprout(self, to_bzrdir, revision_id=None, repository_policy=None): |
33 | + def sprout(self, to_bzrdir, revision_id=None, repository_policy=None, |
34 | + repository=None): |
35 | """Create a new line of development from the branch, into to_bzrdir. |
36 | |
37 | to_bzrdir controls the branch format. |
38 | @@ -1277,7 +1286,7 @@ |
39 | if (repository_policy is not None and |
40 | repository_policy.requires_stacking()): |
41 | to_bzrdir._format.require_stacking(_skip_repo=True) |
42 | - result = to_bzrdir.create_branch() |
43 | + result = to_bzrdir.create_branch(repository=repository) |
44 | result.lock_write() |
45 | try: |
46 | if repository_policy is not None: |
47 | @@ -1634,7 +1643,8 @@ |
48 | hook(params) |
49 | |
50 | def _initialize_helper(self, a_bzrdir, utf8_files, name=None, |
51 | - lock_type='metadir', set_format=True): |
52 | + repository=None, lock_type='metadir', |
53 | + set_format=True): |
54 | """Initialize a branch in a bzrdir, with specified files |
55 | |
56 | :param a_bzrdir: The bzrdir to initialize the branch in |
57 | @@ -1674,11 +1684,12 @@ |
58 | finally: |
59 | if lock_taken: |
60 | control_files.unlock() |
61 | - branch = self.open(a_bzrdir, name, _found=True) |
62 | + branch = self.open(a_bzrdir, name, _found=True, |
63 | + found_repository=repository) |
64 | self._run_post_branch_init_hooks(a_bzrdir, name, branch) |
65 | return branch |
66 | |
67 | - def initialize(self, a_bzrdir, name=None): |
68 | + def initialize(self, a_bzrdir, name=None, repository=None): |
69 | """Create a branch of this format in a_bzrdir. |
70 | |
71 | :param name: Name of the colocated branch to create. |
72 | @@ -1718,7 +1729,8 @@ |
73 | """ |
74 | raise NotImplementedError(self.network_name) |
75 | |
76 | - def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False): |
77 | + def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False, |
78 | + found_repository=None): |
79 | """Return the branch object for a_bzrdir |
80 | |
81 | :param a_bzrdir: A BzrDir that contains a branch. |
82 | @@ -2018,8 +2030,11 @@ |
83 | """See BranchFormat.get_format_description().""" |
84 | return "Branch format 4" |
85 | |
86 | - def initialize(self, a_bzrdir, name=None): |
87 | + def initialize(self, a_bzrdir, name=None, repository=None): |
88 | """Create a branch of this format in a_bzrdir.""" |
89 | + if repository is not None: |
90 | + raise NotImplementedError( |
91 | + "initialize(repository=<not None>) on %r" % (self,)) |
92 | utf8_files = [('revision-history', ''), |
93 | ('branch-name', ''), |
94 | ] |
95 | @@ -2034,16 +2049,19 @@ |
96 | """The network name for this format is the control dirs disk label.""" |
97 | return self._matchingbzrdir.get_format_string() |
98 | |
99 | - def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False): |
100 | + def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False, |
101 | + found_repository=None): |
102 | """See BranchFormat.open().""" |
103 | if not _found: |
104 | # we are being called directly and must probe. |
105 | raise NotImplementedError |
106 | + if found_repository is None: |
107 | + found_repository = a_bzrdir.open_repository() |
108 | return BzrBranch(_format=self, |
109 | _control_files=a_bzrdir._control_files, |
110 | a_bzrdir=a_bzrdir, |
111 | name=name, |
112 | - _repository=a_bzrdir.open_repository()) |
113 | + _repository=found_repository) |
114 | |
115 | def __str__(self): |
116 | return "Bazaar-NG branch format 4" |
117 | @@ -2063,7 +2081,8 @@ |
118 | """ |
119 | return self.get_format_string() |
120 | |
121 | - def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False): |
122 | + def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False, |
123 | + found_repository=None): |
124 | """See BranchFormat.open().""" |
125 | if not _found: |
126 | format = BranchFormat.find_format(a_bzrdir, name=name) |
127 | @@ -2074,11 +2093,13 @@ |
128 | try: |
129 | control_files = lockable_files.LockableFiles(transport, 'lock', |
130 | lockdir.LockDir) |
131 | + if found_repository is None: |
132 | + found_repository = a_bzrdir.find_repository() |
133 | return self._branch_class()(_format=self, |
134 | _control_files=control_files, |
135 | name=name, |
136 | a_bzrdir=a_bzrdir, |
137 | - _repository=a_bzrdir.find_repository(), |
138 | + _repository=found_repository, |
139 | ignore_fallbacks=ignore_fallbacks) |
140 | except errors.NoSuchFile: |
141 | raise errors.NotBranchError(path=transport.base, bzrdir=a_bzrdir) |
142 | @@ -2116,12 +2137,12 @@ |
143 | """See BranchFormat.get_format_description().""" |
144 | return "Branch format 5" |
145 | |
146 | - def initialize(self, a_bzrdir, name=None): |
147 | + def initialize(self, a_bzrdir, name=None, repository=None): |
148 | """Create a branch of this format in a_bzrdir.""" |
149 | utf8_files = [('revision-history', ''), |
150 | ('branch-name', ''), |
151 | ] |
152 | - return self._initialize_helper(a_bzrdir, utf8_files, name) |
153 | + return self._initialize_helper(a_bzrdir, utf8_files, name, repository) |
154 | |
155 | def supports_tags(self): |
156 | return False |
157 | @@ -2149,13 +2170,13 @@ |
158 | """See BranchFormat.get_format_description().""" |
159 | return "Branch format 6" |
160 | |
161 | - def initialize(self, a_bzrdir, name=None): |
162 | + def initialize(self, a_bzrdir, name=None, repository=None): |
163 | """Create a branch of this format in a_bzrdir.""" |
164 | utf8_files = [('last-revision', '0 null:\n'), |
165 | ('branch.conf', ''), |
166 | ('tags', ''), |
167 | ] |
168 | - return self._initialize_helper(a_bzrdir, utf8_files, name) |
169 | + return self._initialize_helper(a_bzrdir, utf8_files, name, repository) |
170 | |
171 | def make_tags(self, branch): |
172 | """See bzrlib.branch.BranchFormat.make_tags().""" |
173 | @@ -2179,14 +2200,14 @@ |
174 | """See BranchFormat.get_format_description().""" |
175 | return "Branch format 8" |
176 | |
177 | - def initialize(self, a_bzrdir, name=None): |
178 | + def initialize(self, a_bzrdir, name=None, repository=None): |
179 | """Create a branch of this format in a_bzrdir.""" |
180 | utf8_files = [('last-revision', '0 null:\n'), |
181 | ('branch.conf', ''), |
182 | ('tags', ''), |
183 | ('references', '') |
184 | ] |
185 | - return self._initialize_helper(a_bzrdir, utf8_files, name) |
186 | + return self._initialize_helper(a_bzrdir, utf8_files, name, repository) |
187 | |
188 | def __init__(self): |
189 | super(BzrBranchFormat8, self).__init__() |
190 | @@ -2215,13 +2236,13 @@ |
191 | This format was introduced in bzr 1.6. |
192 | """ |
193 | |
194 | - def initialize(self, a_bzrdir, name=None): |
195 | + def initialize(self, a_bzrdir, name=None, repository=None): |
196 | """Create a branch of this format in a_bzrdir.""" |
197 | utf8_files = [('last-revision', '0 null:\n'), |
198 | ('branch.conf', ''), |
199 | ('tags', ''), |
200 | ] |
201 | - return self._initialize_helper(a_bzrdir, utf8_files, name) |
202 | + return self._initialize_helper(a_bzrdir, utf8_files, name, repository) |
203 | |
204 | def _branch_class(self): |
205 | return BzrBranch7 |
206 | @@ -2269,7 +2290,8 @@ |
207 | transport = a_bzrdir.get_branch_transport(None, name=name) |
208 | location = transport.put_bytes('location', to_branch.base) |
209 | |
210 | - def initialize(self, a_bzrdir, name=None, target_branch=None): |
211 | + def initialize(self, a_bzrdir, name=None, target_branch=None, |
212 | + repository=None): |
213 | """Create a branch of this format in a_bzrdir.""" |
214 | if target_branch is None: |
215 | # this format does not implement branch itself, thus the implicit |
216 | @@ -2303,7 +2325,8 @@ |
217 | return clone |
218 | |
219 | def open(self, a_bzrdir, name=None, _found=False, location=None, |
220 | - possible_transports=None, ignore_fallbacks=False): |
221 | + possible_transports=None, ignore_fallbacks=False, |
222 | + found_repository=None): |
223 | """Return the branch that the branch reference in a_bzrdir points at. |
224 | |
225 | :param a_bzrdir: A BzrDir that contains a branch. |
226 | |
227 | === modified file 'bzrlib/bzrdir.py' |
228 | --- bzrlib/bzrdir.py 2010-12-07 09:06:39 +0000 |
229 | +++ bzrlib/bzrdir.py 2010-12-15 00:41:26 +0000 |
230 | @@ -1027,8 +1027,11 @@ |
231 | tree.clone(result) |
232 | return result |
233 | |
234 | - def create_branch(self, name=None): |
235 | + def create_branch(self, name=None, repository=None): |
236 | """See BzrDir.create_branch.""" |
237 | + if repository is not None: |
238 | + raise NotImplementedError( |
239 | + "create_branch(repository=<not None>) on %r" % (self,)) |
240 | return self._format.get_branch_format().initialize(self, name=name) |
241 | |
242 | def destroy_branch(self, name=None): |
243 | @@ -1264,9 +1267,10 @@ |
244 | """See BzrDir.can_convert_format().""" |
245 | return True |
246 | |
247 | - def create_branch(self, name=None): |
248 | + def create_branch(self, name=None, repository=None): |
249 | """See BzrDir.create_branch.""" |
250 | - return self._format.get_branch_format().initialize(self, name=name) |
251 | + return self._format.get_branch_format().initialize(self, name=name, |
252 | + repository=repository) |
253 | |
254 | def destroy_branch(self, name=None): |
255 | """See BzrDir.create_branch.""" |
256 | |
257 | === modified file 'bzrlib/controldir.py' |
258 | --- bzrlib/controldir.py 2010-09-10 09:46:15 +0000 |
259 | +++ bzrlib/controldir.py 2010-12-15 00:41:26 +0000 |
260 | @@ -27,11 +27,10 @@ |
261 | import textwrap |
262 | |
263 | from bzrlib import ( |
264 | + cleanup, |
265 | errors, |
266 | graph, |
267 | - registry, |
268 | revision as _mod_revision, |
269 | - symbol_versioning, |
270 | urlutils, |
271 | ) |
272 | from bzrlib.push import ( |
273 | @@ -47,6 +46,8 @@ |
274 | |
275 | """) |
276 | |
277 | +from bzrlib import registry |
278 | + |
279 | |
280 | class ControlComponent(object): |
281 | """Abstract base class for control directory components. |
282 | @@ -143,7 +144,7 @@ |
283 | """Destroy the repository in this ControlDir.""" |
284 | raise NotImplementedError(self.destroy_repository) |
285 | |
286 | - def create_branch(self, name=None): |
287 | + def create_branch(self, name=None, repository=None): |
288 | """Create a branch in this ControlDir. |
289 | |
290 | :param name: Name of the colocated branch to create, None for |
291 | @@ -364,6 +365,19 @@ |
292 | :param create_tree_if_local: If true, a working-tree will be created |
293 | when working locally. |
294 | """ |
295 | + operation = cleanup.OperationWithCleanups(self._sprout) |
296 | + return operation.run(url, revision_id=revision_id, |
297 | + force_new_repo=force_new_repo, recurse=recurse, |
298 | + possible_transports=possible_transports, |
299 | + accelerator_tree=accelerator_tree, hardlink=hardlink, |
300 | + stacked=stacked, source_branch=source_branch, |
301 | + create_tree_if_local=create_tree_if_local) |
302 | + |
303 | + def _sprout(self, op, url, revision_id=None, force_new_repo=False, |
304 | + recurse='down', possible_transports=None, |
305 | + accelerator_tree=None, hardlink=False, stacked=False, |
306 | + source_branch=None, create_tree_if_local=True): |
307 | + add_cleanup = op.add_cleanup |
308 | target_transport = get_transport(url, possible_transports) |
309 | target_transport.ensure_base() |
310 | cloning_format = self.cloning_metadir(stacked) |
311 | @@ -373,6 +387,7 @@ |
312 | # even if the origin was stacked |
313 | stacked_branch_url = None |
314 | if source_branch is not None: |
315 | + add_cleanup(source_branch.lock_read().unlock) |
316 | if stacked: |
317 | stacked_branch_url = self.root_transport.base |
318 | source_repository = source_branch.repository |
319 | @@ -388,9 +403,14 @@ |
320 | source_repository = self.open_repository() |
321 | except errors.NoRepositoryPresent: |
322 | source_repository = None |
323 | + else: |
324 | + add_cleanup(source_repository.lock_read().unlock) |
325 | + else: |
326 | + add_cleanup(source_branch.lock_read().unlock) |
327 | repository_policy = result.determine_repository_policy( |
328 | force_new_repo, stacked_branch_url, require_stacking=stacked) |
329 | result_repo, is_new_repo = repository_policy.acquire_repository() |
330 | + add_cleanup(result_repo.lock_write().unlock) |
331 | is_stacked = stacked or (len(result_repo._fallback_repositories) != 0) |
332 | if is_new_repo and revision_id is not None and not is_stacked: |
333 | fetch_spec = graph.PendingAncestryResult( |
334 | @@ -412,7 +432,8 @@ |
335 | result_branch = result.create_branch() |
336 | else: |
337 | result_branch = source_branch.sprout(result, |
338 | - revision_id=revision_id, repository_policy=repository_policy) |
339 | + revision_id=revision_id, repository_policy=repository_policy, |
340 | + repository=result_repo) |
341 | mutter("created new branch %r" % (result_branch,)) |
342 | |
343 | # Create/update the result working tree |
344 | @@ -420,7 +441,7 @@ |
345 | isinstance(target_transport, local.LocalTransport) and |
346 | (result_repo is None or result_repo.make_working_trees())): |
347 | wt = result.create_workingtree(accelerator_tree=accelerator_tree, |
348 | - hardlink=hardlink) |
349 | + hardlink=hardlink, from_branch=result_branch) |
350 | wt.lock_write() |
351 | try: |
352 | if wt.path2id('') is None: |
353 | |
354 | === modified file 'bzrlib/remote.py' |
355 | --- bzrlib/remote.py 2010-12-02 10:41:05 +0000 |
356 | +++ bzrlib/remote.py 2010-12-15 00:41:26 +0000 |
357 | @@ -33,6 +33,7 @@ |
358 | revision as _mod_revision, |
359 | static_tuple, |
360 | symbol_versioning, |
361 | + urlutils, |
362 | ) |
363 | from bzrlib.branch import BranchReferenceFormat, BranchWriteLockResult |
364 | from bzrlib.bzrdir import BzrDir, RemoteBzrDirFormat |
365 | @@ -246,14 +247,17 @@ |
366 | self._ensure_real() |
367 | self._real_bzrdir.destroy_repository() |
368 | |
369 | - def create_branch(self, name=None): |
370 | + def create_branch(self, name=None, repository=None): |
371 | # as per meta1 formats - just delegate to the format object which may |
372 | # be parameterised. |
373 | real_branch = self._format.get_branch_format().initialize(self, |
374 | - name=name) |
375 | + name=name, repository=repository) |
376 | if not isinstance(real_branch, RemoteBranch): |
377 | - result = RemoteBranch(self, self.find_repository(), real_branch, |
378 | - name=name) |
379 | + if not isinstance(repository, RemoteRepository): |
380 | + raise AssertionError( |
381 | + 'need a RemoteRepository to use with RemoteBranch, got %r' |
382 | + % (repository,)) |
383 | + result = RemoteBranch(self, repository, real_branch, name=name) |
384 | else: |
385 | result = real_branch |
386 | # BzrDir.clone_on_transport() uses the result of create_branch but does |
387 | @@ -2093,7 +2097,7 @@ |
388 | name=name) |
389 | return result |
390 | |
391 | - def initialize(self, a_bzrdir, name=None): |
392 | + def initialize(self, a_bzrdir, name=None, repository=None): |
393 | # 1) get the network name to use. |
394 | if self._custom_format: |
395 | network_name = self._custom_format.network_name() |
396 | @@ -2127,13 +2131,25 @@ |
397 | # Turn the response into a RemoteRepository object. |
398 | format = RemoteBranchFormat(network_name=response[1]) |
399 | repo_format = response_tuple_to_repo_format(response[3:]) |
400 | - if response[2] == '': |
401 | - repo_bzrdir = a_bzrdir |
402 | + repo_path = response[2] |
403 | + if repository is not None: |
404 | + remote_repo_url = urlutils.join(medium.base, repo_path) |
405 | + url_diff = urlutils.relative_url(repository.user_url, |
406 | + remote_repo_url) |
407 | + if url_diff != '.': |
408 | + raise AssertionError( |
409 | + 'repository.user_url %r does not match URL from server ' |
410 | + 'response (%r + %r)' |
411 | + % (repository.user_url, medium.base, repo_path)) |
412 | + remote_repo = repository |
413 | else: |
414 | - repo_bzrdir = RemoteBzrDir( |
415 | - a_bzrdir.root_transport.clone(response[2]), a_bzrdir._format, |
416 | - a_bzrdir._client) |
417 | - remote_repo = RemoteRepository(repo_bzrdir, repo_format) |
418 | + if repo_path == '': |
419 | + repo_bzrdir = a_bzrdir |
420 | + else: |
421 | + repo_bzrdir = RemoteBzrDir( |
422 | + a_bzrdir.root_transport.clone(repo_path), a_bzrdir._format, |
423 | + a_bzrdir._client) |
424 | + remote_repo = RemoteRepository(repo_bzrdir, repo_format) |
425 | remote_branch = RemoteBranch(a_bzrdir, remote_repo, |
426 | format=format, setup_stacking=False, name=name) |
427 | # XXX: We know this is a new branch, so it must have revno 0, revid |
428 | |
429 | === modified file 'bzrlib/tests/blackbox/test_branch.py' |
430 | --- bzrlib/tests/blackbox/test_branch.py 2010-12-02 10:41:05 +0000 |
431 | +++ bzrlib/tests/blackbox/test_branch.py 2010-12-15 00:41:26 +0000 |
432 | @@ -414,7 +414,7 @@ |
433 | # being too low. If rpc_count increases, more network roundtrips have |
434 | # become necessary for this use case. Please do not adjust this number |
435 | # upwards without agreement from bzr's network support maintainers. |
436 | - self.assertLength(37, self.hpss_calls) |
437 | + self.assertLength(36, self.hpss_calls) |
438 | |
439 | def test_branch_from_trivial_branch_streaming_acceptance(self): |
440 | self.setup_smart_server_with_call_log() |
441 | |
442 | === modified file 'bzrlib/tests/test_branch.py' |
443 | --- bzrlib/tests/test_branch.py 2010-12-07 09:06:39 +0000 |
444 | +++ bzrlib/tests/test_branch.py 2010-12-15 00:41:26 +0000 |
445 | @@ -113,7 +113,7 @@ |
446 | """See BzrBranchFormat.get_format_string().""" |
447 | return "Sample branch format." |
448 | |
449 | - def initialize(self, a_bzrdir, name=None): |
450 | + def initialize(self, a_bzrdir, name=None, repository=None): |
451 | """Format 4 branches cannot be created.""" |
452 | t = a_bzrdir.get_branch_transport(self, name=name) |
453 | t.put_bytes('format', self.get_format_string()) |
454 | |
455 | === modified file 'bzrlib/tests/test_bzrdir.py' |
456 | --- bzrlib/tests/test_bzrdir.py 2010-10-15 14:21:03 +0000 |
457 | +++ bzrlib/tests/test_bzrdir.py 2010-12-15 00:41:26 +0000 |
458 | @@ -29,6 +29,7 @@ |
459 | controldir, |
460 | errors, |
461 | help_topics, |
462 | + lock, |
463 | repository, |
464 | osutils, |
465 | remote, |
466 | @@ -1349,6 +1350,12 @@ |
467 | def set_parent(self, parent): |
468 | self._parent = parent |
469 | |
470 | + def lock_read(self): |
471 | + return lock.LogicalLockResult(self.unlock) |
472 | + |
473 | + def unlock(self): |
474 | + return |
475 | + |
476 | |
477 | class TestBzrDirSprout(TestCaseWithMemoryTransport): |
478 | |
479 | |
480 | === modified file 'bzrlib/tests/test_foreign.py' |
481 | --- bzrlib/tests/test_foreign.py 2010-08-02 18:36:31 +0000 |
482 | +++ bzrlib/tests/test_foreign.py 2010-12-15 00:41:26 +0000 |
483 | @@ -173,17 +173,19 @@ |
484 | super(DummyForeignVcsBranchFormat, self).__init__() |
485 | self._matchingbzrdir = DummyForeignVcsDirFormat() |
486 | |
487 | - def open(self, a_bzrdir, name=None, _found=False): |
488 | + def open(self, a_bzrdir, name=None, _found=False, found_repository=None): |
489 | if not _found: |
490 | raise NotImplementedError |
491 | try: |
492 | transport = a_bzrdir.get_branch_transport(None, name=name) |
493 | control_files = lockable_files.LockableFiles(transport, 'lock', |
494 | lockdir.LockDir) |
495 | + if found_repository is None: |
496 | + found_repository = a_bzrdir.find_repository() |
497 | return DummyForeignVcsBranch(_format=self, |
498 | _control_files=control_files, |
499 | a_bzrdir=a_bzrdir, |
500 | - _repository=a_bzrdir.find_repository()) |
501 | + _repository=found_repository) |
502 | except errors.NoSuchFile: |
503 | raise errors.NotBranchError(path=transport.base) |
504 | |
505 | |
506 | === modified file 'doc/en/release-notes/bzr-2.3.txt' |
507 | --- doc/en/release-notes/bzr-2.3.txt 2010-12-14 23:00:51 +0000 |
508 | +++ doc/en/release-notes/bzr-2.3.txt 2010-12-15 00:41:26 +0000 |
509 | @@ -31,6 +31,10 @@ |
510 | missing inventories. This removes at least one network roundtrip when |
511 | pushing to a stacked branch. (Andrew Bennetts) |
512 | |
513 | +* ``ControlDir.sprout`` no longer opens the target repository more than |
514 | + once. This avoids some unnecessary IO, and removes a network roundtrip |
515 | + when doing ``bzr branch`` to a smart server URL. (Andrew Bennetts) |
516 | + |
517 | * ``bzr resolve`` now accepts ``--take-this`` and ``--take-other`` actions |
518 | for text conflicts. This *replace* the whole file with the content |
519 | designated by the action. This will *ignore* all differences that would |
520 | @@ -53,6 +57,14 @@ |
521 | .. Changes that may require updates in plugins or other code that uses |
522 | bzrlib. |
523 | |
524 | +* ``Branch.sprout``, ``BranchFormat.initalize`` and |
525 | + ``ControlDir.create_branch`` now take an optional ``repository`` keyword |
526 | + argument, and ``BranchFormat.open`` now takes an optional |
527 | + ``found_repository`` keyword argument. These provide the repository |
528 | + object for new branch object to use (for cases when the caller has |
529 | + already opened that repository). Implementations of these APIs will |
530 | + need to be updated to accept these arguments. (Andrew Bennetts) |
531 | + |
532 | Internals |
533 | ********* |
534 | |
535 | |
536 | === modified file 'doc/en/whats-new/whats-new-in-2.3.txt' |
537 | --- doc/en/whats-new/whats-new-in-2.3.txt 2010-12-14 09:33:53 +0000 |
538 | +++ doc/en/whats-new/whats-new-in-2.3.txt 2010-12-15 00:41:26 +0000 |
539 | @@ -69,8 +69,9 @@ |
540 | * ``bzr send`` uses less memory. |
541 | (John Arbash Meinel, #614576) |
542 | |
543 | -* Fetches involving stacked branches and branches with tags are now faster. |
544 | - Some redundant network reads were removed. (Andrew Bennetts) |
545 | +* Fetches involving stacked branches and branches with tags now do slightly less |
546 | + I/O, and so does branching from an existing branch. This also improves the |
547 | + network performance of these operations. (Andrew Bennetts) |
548 | |
549 | * Inventory entries now consume less memory (on 32-bit Ubuntu file entries |
550 | have dropped from 68 bytes to 40, and directory entries from 120 bytes |
Sorry about the delay on this. In general, I really like the idea of this patch.
I would at least mutter a warning about the double fallback, we don't have to display it on the screen. It would be nice to fix it, but you don't have to get everything right on the first attempt.
I really don't like the proliferation of "no, really, use this object" parameters. I think the problem is that BzrDir.sprout() wants to be the master, but it still defers some of the work to Branch/ Repo/WorkingTre e. I don't have a better answer, though. I know you mentioned this, too.
I'd personally really like to see a: write_locked( create= True)
X.open_
Or something along those lines.
I think part of the confusion is also that if you got something which is a Branch, and you think there is a tree there, the standard use is: bzrdir. open_workingtre e()
branch.
However, that *reopens* the Branch and Repository.
We also have a lot of code that does stuff like:
a) If there is a WT, do X
b) If not, but there is a Branch, do Y
c) If not, but there is a Repository, do Z
It would be nice to simplify a lot of that if we could. That is some of what "BzrDir. open_containing _tree_or_ branch" is supposed to be. Though many times I want "open_containin g_tree_ or_direct_ branch" (don't recurse for a branch, but you can recurse for a Tree).
This is potentially disruptive to bzr-svn, because it adds a new parameter that BzrDir will be passing down the stack. As such, adding a cleaner new api may be an easier way to get what we want. It would let us add a compatability shim for objects that don't implement the new interface, and could be cleaner overall.
Then again, the practical side of me says, you've done the work, it works, even if it isn't perfect, it is better than what we have.