Merge lp:~vila/bzr/cached-branch-store into lp:bzr

Proposed by Vincent Ladeuil
Status: Merged
Approved by: Martin Packman
Approved revision: no longer in the source branch.
Merged at revision: 6468
Proposed branch: lp:~vila/bzr/cached-branch-store
Merge into: lp:bzr
Diff against target: 1574 lines (+343/-213)
33 files modified
bzrlib/branch.py (+22/-9)
bzrlib/builtins.py (+12/-5)
bzrlib/config.py (+48/-19)
bzrlib/controldir.py (+3/-1)
bzrlib/foreign.py (+2/-1)
bzrlib/plugins/launchpad/test_lp_open.py (+11/-9)
bzrlib/push.py (+2/-1)
bzrlib/remote.py (+19/-18)
bzrlib/switch.py (+10/-6)
bzrlib/tests/blackbox/test_bound_branches.py (+11/-2)
bzrlib/tests/blackbox/test_checkout.py (+1/-1)
bzrlib/tests/blackbox/test_commit.py (+1/-1)
bzrlib/tests/blackbox/test_dpush.py (+4/-5)
bzrlib/tests/blackbox/test_info.py (+3/-3)
bzrlib/tests/blackbox/test_log.py (+3/-3)
bzrlib/tests/blackbox/test_merge.py (+17/-5)
bzrlib/tests/blackbox/test_merge_directive.py (+2/-1)
bzrlib/tests/blackbox/test_pull.py (+11/-5)
bzrlib/tests/blackbox/test_push.py (+15/-9)
bzrlib/tests/blackbox/test_reconfigure.py (+10/-10)
bzrlib/tests/blackbox/test_send.py (+9/-10)
bzrlib/tests/blackbox/test_switch.py (+5/-2)
bzrlib/tests/blackbox/test_whoami.py (+12/-10)
bzrlib/tests/per_branch/test_branch.py (+1/-17)
bzrlib/tests/per_branch/test_break_lock.py (+3/-2)
bzrlib/tests/per_branch/test_create_clone.py (+1/-2)
bzrlib/tests/test_branch.py (+3/-3)
bzrlib/tests/test_config.py (+34/-3)
bzrlib/tests/test_directory_service.py (+21/-23)
bzrlib/tests/test_info.py (+9/-5)
bzrlib/tests/test_reconfigure.py (+16/-4)
bzrlib/tests/test_remote.py (+16/-15)
bzrlib/tests/test_smart.py (+6/-3)
To merge this branch: bzr merge lp:~vila/bzr/cached-branch-store
Reviewer Review Type Date Requested Status
Martin Packman (community) Approve
Review via email: mp+87985@code.launchpad.net

Commit message

Cache the branch config store to avoid useless IOs.

Description of the change

This is it !

Well, this doesn't fix bug #832042 yet (the local config files still writes
the config file on every option change but the related fix will be 1) far
simpler than this one, 2) raises different but simpler issues. So I focus on
the branch config here).

Summary: Instead of writing the branch.conf config file on each
modification, it keeps track of the modifications and write the file when
the branch is unlocked (reading it again to guard against users of the old
config API).

I'm confident this proposal is technically correct both in the
implementation and in the implied code and test fixes introduced by the
change in locking. Yet, while these fixes are quite easy once you understand
the issues, it seems a bit risky to land it so late in the series
cycle. Something is missing for that and I'd like feedback about it.

Long story:

To avoid writing the config file for every branch option change, all the
changes are batched and the write happens at the last possible time.

Since this is a write operation on a branch, it requires a locked
branch. Therefore the write happens when the branch is unlocked.

I can't describe it more concisely but this doesn't clearly expose the
fallouts.

The real question here is: when should the branch be *locked* ?

There are two possible answers to that:

1 - before the first change is attempted (set() or remove()),

2 - before the config file needs to be saved.

In the actual implementation (and that's true for both the configobj based
implementation and the stack based one), *every* change takes a lock on the
branch *and* write the file (this is implemented in _CompatibleStack so it
applies to the local config files as well except they use a lockdir instead
of a branch of course).

Avoiding these writes means we keep the changes in memory. As such the
branch doesn't need to be locked which makes (2) compelling.

The main drawback is of course that these changes are not saved to disk and
the main consequence is that blackbox tests become broken: modifying an
option in a branch object is not enough to make it available to a subsequent
run_bzr() call. All config changes should now happen in an explicitely
locked branch.

Likewise, if the run_bzr() call modifies a config option, the config file
needs to be reloaded to see this change. This is a bit more suprising and
requires an additional explanation: the *old* design were reading the config
file for each option and as such was immune from this issue. The new design
caches the config file so a change introduced by a run_bzr() call is not
seen. This happens when a branch config option is migrated to the new design
but not for the other options. It really depends on how and when they are
used but all the cases I've encountered so far make sense, see
bb.test_bound_branches for example where calling run_bzr(unbind) requires
acquiring the branch (via its wt) *after* the run rather than before to get
the right behavior. The changes in test_bound_branches are required as soon
as 'bound_location' is using the new design *and* this proposal. Note that
some tests were already aware of similar issues: search for 're-open tree as
external run_bzr modified it' in bb.test_merge for such examples.

So, not a new issue but an almost unknown one that becomes far more common
with this proposal.

Back to the proposal, you'll see four kind of changes:

- implement the caching for the config store in Branch and RemoteBranch,
  this includes sharing the same store between a RemoteBranch and its
  related _real_branch (the core of this proposal),

- fixes to code so the option changes are saved to disk at the right time
  Converter5to6, Converter6to7 in branch.py, cmd_pull, cmd_init, cmd_whoami,
  cmd_merge_directive in builtins.py, cmd_config in config.py,
  controldir.ControlComponent.push_branch, cmd_dpush in foreign.py for
  examples. Special note for the infamous stacked_on_url that needs to be
  seen by both the client and the server in all cases.

- fixes blackbox tests that requires a locked branch for option changes to
  be saved on disk (the idiom is an added lock_write(branch)/try:set
  options/finally: unlock(). Suggestions welcome to make this easier to
  spell, a context manager thingy to do with locked(branch): ?

- fixes blackbox tests that requires a fresh object to observe option
  changes, the idiom is br = br.bzrdir.open_branch()

So the missing bit I alluded in the introduction is probably something along
the following lines:

- set()/remove() requires a lock and triggers a config file write for
  backward compatibility

- we fix the spurious writes above by locking the branch at a higher level
  when/where needed as I did here both in code and in tests.

- add a reload() method to stacks so blackbox tests can reload only the
  config stack instead of the whole object.

I deleted
bzrlib.tests.per_branch.test_branch.TestBound.test_unlocked_does_not_cache_master_branch
as the trick used in the test is unlikely to be used in real life and not
valid anymore anyway.

To post a comment you must log in.
Revision history for this message
Vincent Ladeuil (vila) wrote :

Change set/remove to require a lock for the branch config files.

This means that tests (or any plugin for that matter) do not requires an
explicit lock on the branch anymore to change a single option. This also
means the optimisation becomes "opt-in" and as such won't be as
spectacular as it may be and/or harder to get right (nothing fails
anymore).

This reduces the diff by ~300 lines.

Code/tests that were updating more than one config option is still taking
a lock to at least avoid some IOs and demonstrate the benefits through
the decreased number of hpss calls.

The duplication between BranchStack and BranchOnlyStack will be removed
once the same sharing is in place for local config files, at which point
the Stack class itself may be able to host the changes.

If nothing else, once the same change has been made to the local config
files the BranchOnlyStack will be come useless since it's intended purpose
was more to avoid the IOs associated with the locl config files than
forbidding setting stacked_on_location in locl config files (while setting
stacked_on_location from a local file may sound weird, it still makes sense
to allow it on the server side (launchpad excluded given its implementation)
or even for expert users (including from the command line with -O) in
specific cases).

Overall removing the constraint on branch locking sounds less risky to land
especially at the beginning of the 2.6 cycle.

So, please review :)

I'll make a further proposal on top of this one for sharing the local config files.

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

Forgot to mention: I didn't implement .reload() for stacks, not enough tests will benefit from that and that still needs to be fixed on a case by case basis (where/when it fails).

In the wild, there shouldn't be much cases of people using the new config API while relying on the config files to magically refresh themselves (or expecting to observe a side effect of a bzr command in a different process via a config option).

Revision history for this message
Martin Packman (gz) wrote :

Much nicer with the reduced lock/unlock requirement, and approach seems good overall.

One thing I'm not clear on is whether all the branch methods like Branch.set_submit_branch and other config writing helper methods also want the @needs_write_lock decorator. I guess as BranchStack mutating methods do that anyway so it's unimportant.

If a plugin author wants to reduce the amount of pointless config-writing in their code, how would they go about tracking down where they need to add locking?

Documenting in what's new and landing on 2.5 seems required to me.

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

Thanks a lot for the review !

> Much nicer with the reduced lock/unlock requirement, and approach seems good
> overall.
>
> One thing I'm not clear on is whether all the branch methods like
> Branch.set_submit_branch and other config writing helper methods also want the
> @needs_write_lock decorator.

No, because ultimately they will call stack.set().

> I guess as BranchStack mutating methods do that
> anyway so it's unimportant.

Indeed.

>
> If a plugin author wants to reduce the amount of pointless config-writing in
> their code, how would they go about tracking down where they need to add
> locking?

That's the main loss of making the behavior "opt-in". There is no obvious failure anymore.

As plugins migrate to the new scheme we'll get more feedback ?

>
> Documenting in what's new and landing on 2.5 seems required to me.

I need to think a bit more about it (I'd like a full fix for bug #832042 first which I'm working on right now, mainly the local config stores should be cached too).

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

Branch refreshed, will land to trunk

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

Funnily enough, merging trunk made 3 tests fail. Cause ? Less hpss calls !

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

sent to pqm by email

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'bzrlib/branch.py'
2--- bzrlib/branch.py 2012-01-27 14:56:06 +0000
3+++ bzrlib/branch.py 2012-02-14 17:36:32 +0000
4@@ -1,4 +1,4 @@
5-# Copyright (C) 2005-2011 Canonical Ltd
6+# Copyright (C) 2005-2012 Canonical Ltd
7 #
8 # This program is free software; you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10@@ -2481,6 +2481,7 @@
11 self.control_files = _control_files
12 self._transport = _control_files._transport
13 self.repository = _repository
14+ self.conf_store = None
15 Branch.__init__(self, possible_transports)
16
17 def __str__(self):
18@@ -2502,7 +2503,9 @@
19 return _mod_config.TransportConfig(self._transport, 'branch.conf')
20
21 def _get_config_store(self):
22- return _mod_config.BranchStore(self)
23+ if self.conf_store is None:
24+ self.conf_store = _mod_config.BranchStore(self)
25+ return self.conf_store
26
27 def is_locked(self):
28 return self.control_files.is_locked()
29@@ -2557,6 +2560,8 @@
30
31 @only_raises(errors.LockNotHeld, errors.LockBroken)
32 def unlock(self):
33+ if self.conf_store is not None:
34+ self.conf_store.save_changes()
35 try:
36 self.control_files.unlock()
37 finally:
38@@ -3245,9 +3250,13 @@
39
40 # Copy source data into target
41 new_branch._write_last_revision_info(*branch.last_revision_info())
42- new_branch.set_parent(branch.get_parent())
43- new_branch.set_bound_location(branch.get_bound_location())
44- new_branch.set_push_location(branch.get_push_location())
45+ new_branch.lock_write()
46+ try:
47+ new_branch.set_parent(branch.get_parent())
48+ new_branch.set_bound_location(branch.get_bound_location())
49+ new_branch.set_push_location(branch.get_push_location())
50+ finally:
51+ new_branch.unlock()
52
53 # New branch has no tags by default
54 new_branch.tags._set_tag_dict({})
55@@ -3259,11 +3268,15 @@
56
57 # Clean up old files
58 new_branch._transport.delete('revision-history')
59+ branch.lock_write()
60 try:
61- branch.set_parent(None)
62- except errors.NoSuchFile:
63- pass
64- branch.set_bound_location(None)
65+ try:
66+ branch.set_parent(None)
67+ except errors.NoSuchFile:
68+ pass
69+ branch.set_bound_location(None)
70+ finally:
71+ branch.unlock()
72
73
74 class Converter6to7(object):
75
76=== modified file 'bzrlib/builtins.py'
77--- bzrlib/builtins.py 2012-02-07 13:18:40 +0000
78+++ bzrlib/builtins.py 2012-02-14 17:36:32 +0000
79@@ -1,4 +1,4 @@
80-# Copyright (C) 2005-2011 Canonical Ltd
81+# Copyright (C) 2005-2012 Canonical Ltd
82 #
83 # This program is free software; you can redistribute it and/or modify
84 # it under the terms of the GNU General Public License as published by
85@@ -1205,6 +1205,8 @@
86 # Remembers if asked explicitly or no previous location is set
87 if (remember
88 or (remember is None and branch_to.get_parent() is None)):
89+ # FIXME: This shouldn't be done before the pull
90+ # succeeds... -- vila 2012-01-02
91 branch_to.set_parent(branch_from.base)
92
93 if revision is not None:
94@@ -3861,7 +3863,9 @@
95 if directory is None:
96 c = Branch.open_containing(u'.')[0].get_config_stack()
97 else:
98- c = Branch.open(directory).get_config_stack()
99+ b = Branch.open(directory)
100+ self.add_cleanup(b.lock_write().unlock)
101+ c = b.get_config_stack()
102 else:
103 c = _mod_config.GlobalStack()
104 c.set('email', name)
105@@ -5261,10 +5265,12 @@
106 else:
107 if location is None:
108 if b.get_bound_location() is not None:
109- raise errors.BzrCommandError(gettext('Branch is already bound'))
110+ raise errors.BzrCommandError(
111+ gettext('Branch is already bound'))
112 else:
113- raise errors.BzrCommandError(gettext('No location supplied '
114- 'and no previous location known'))
115+ raise errors.BzrCommandError(
116+ gettext('No location supplied'
117+ ' and no previous location known'))
118 b_other = Branch.open(location)
119 try:
120 b.bind(b_other)
121@@ -5680,6 +5686,7 @@
122 if public_branch is None:
123 public_branch = stored_public_branch
124 elif stored_public_branch is None:
125+ # FIXME: Should be done only if we succeed ? -- vila 2012-01-03
126 branch.set_public_branch(public_branch)
127 if not include_bundle and public_branch is None:
128 raise errors.BzrCommandError(gettext('No public branch specified or'
129
130=== modified file 'bzrlib/config.py'
131--- bzrlib/config.py 2012-02-07 00:49:58 +0000
132+++ bzrlib/config.py 2012-02-14 17:36:32 +0000
133@@ -3416,20 +3416,6 @@
134 self.branch = branch
135 self.id = 'branch'
136
137- def lock_write(self, token=None):
138- return self.branch.lock_write(token)
139-
140- def unlock(self):
141- return self.branch.unlock()
142-
143- @needs_write_lock
144- def save(self):
145- # We need to be able to override the undecorated implementation
146- self.save_without_locking()
147-
148- def save_without_locking(self):
149- super(BranchStore, self).save()
150-
151
152 class ControlStore(LockableIniFileStore):
153
154@@ -3939,7 +3925,7 @@
155 lstore, mutable_section_id=location)
156
157
158-class BranchStack(_CompatibleStack):
159+class BranchStack(Stack):
160 """Per-location options falling back to branch then global options stack.
161
162 The following sections are queried:
163@@ -3970,6 +3956,24 @@
164 bstore)
165 self.branch = branch
166
167+ def lock_write(self, token=None):
168+ return self.branch.lock_write(token)
169+
170+ def unlock(self):
171+ return self.branch.unlock()
172+
173+ @needs_write_lock
174+ def set(self, name, value):
175+ super(BranchStack, self).set(name, value)
176+ # Unlocking the branch will trigger a store.save_changes() so the last
177+ # unlock saves all the changes.
178+
179+ @needs_write_lock
180+ def remove(self, name):
181+ super(BranchStack, self).remove(name)
182+ # Unlocking the branch will trigger a store.save_changes() so the last
183+ # unlock saves all the changes.
184+
185
186 class RemoteControlStack(_CompatibleStack):
187 """Remote control-only options stack."""
188@@ -3986,7 +3990,7 @@
189 self.bzrdir = bzrdir
190
191
192-class BranchOnlyStack(_CompatibleStack):
193+class BranchOnlyStack(Stack):
194 """Branch-only options stack."""
195
196 # FIXME: _BranchOnlyStack only uses branch.conf and is used only for the
197@@ -4000,6 +4004,24 @@
198 bstore)
199 self.branch = branch
200
201+ def lock_write(self, token=None):
202+ return self.branch.lock_write(token)
203+
204+ def unlock(self):
205+ return self.branch.unlock()
206+
207+ @needs_write_lock
208+ def set(self, name, value):
209+ super(BranchOnlyStack, self).set(name, value)
210+ # Force a write to persistent storage
211+ self.store.save_changes()
212+
213+ @needs_write_lock
214+ def remove(self, name):
215+ super(BranchOnlyStack, self).remove(name)
216+ # Force a write to persistent storage
217+ self.store.save_changes()
218+
219
220 # Use a an empty dict to initialize an empty configobj avoiding all
221 # parsing and encoding checks
222@@ -4072,12 +4094,15 @@
223 # Set the option value
224 self._set_config_option(name, value, directory, scope)
225
226- def _get_stack(self, directory, scope=None):
227+ def _get_stack(self, directory, scope=None, write_access=False):
228 """Get the configuration stack specified by ``directory`` and ``scope``.
229
230 :param directory: Where the configurations are derived from.
231
232 :param scope: A specific config to start from.
233+
234+ :param write_access: Whether a write access to the stack will be
235+ attempted.
236 """
237 # FIXME: scope should allow access to plugin-specific stacks (even
238 # reduced to the plugin-specific store), related to
239@@ -4091,6 +4116,8 @@
240 (_, br, _) = (
241 controldir.ControlDir.open_containing_tree_or_branch(
242 directory))
243+ if write_access:
244+ self.add_cleanup(br.lock_write().unlock)
245 return br.get_config_stack()
246 raise errors.NoSuchConfig(scope)
247 else:
248@@ -4098,6 +4125,8 @@
249 (_, br, _) = (
250 controldir.ControlDir.open_containing_tree_or_branch(
251 directory))
252+ if write_access:
253+ self.add_cleanup(br.lock_write().unlock)
254 return br.get_config_stack()
255 except errors.NotBranchError:
256 return LocationStack(directory)
257@@ -4148,14 +4177,14 @@
258 self.outf.write(' %s = %s\n' % (oname, value))
259
260 def _set_config_option(self, name, value, directory, scope):
261- conf = self._get_stack(directory, scope)
262+ conf = self._get_stack(directory, scope, write_access=True)
263 conf.set(name, value)
264
265 def _remove_config_option(self, name, directory, scope):
266 if name is None:
267 raise errors.BzrCommandError(
268 '--remove expects an option to remove.')
269- conf = self._get_stack(directory, scope)
270+ conf = self._get_stack(directory, scope, write_access=True)
271 try:
272 conf.remove(name)
273 except KeyError:
274
275=== modified file 'bzrlib/controldir.py'
276--- bzrlib/controldir.py 2012-01-31 15:43:17 +0000
277+++ bzrlib/controldir.py 2012-02-14 17:36:32 +0000
278@@ -1,4 +1,4 @@
279-# Copyright (C) 2010, 2011 Canonical Ltd
280+# Copyright (C) 2010, 2011, 2012 Canonical Ltd
281 #
282 # This program is free software; you can redistribute it and/or modify
283 # it under the terms of the GNU General Public License as published by
284@@ -406,6 +406,7 @@
285 repository_to.fetch(source.repository, revision_id=revision_id)
286 br_to = source.clone(self, revision_id=revision_id)
287 if source.get_push_location() is None or remember:
288+ # FIXME: Should be done only if we succeed ? -- vila 2012-01-18
289 source.set_push_location(br_to.base)
290 push_result.stacked_on = None
291 push_result.branch_push_result = None
292@@ -417,6 +418,7 @@
293 else:
294 # We have successfully opened the branch, remember if necessary:
295 if source.get_push_location() is None or remember:
296+ # FIXME: Should be done only if we succeed ? -- vila 2012-01-18
297 source.set_push_location(br_to.base)
298 try:
299 tree_to = self.open_workingtree()
300
301=== modified file 'bzrlib/foreign.py'
302--- bzrlib/foreign.py 2011-12-19 13:23:58 +0000
303+++ bzrlib/foreign.py 2012-02-14 17:36:32 +0000
304@@ -1,4 +1,4 @@
305-# Copyright (C) 2008-2011 Canonical Ltd
306+# Copyright (C) 2008-2012 Canonical Ltd
307 #
308 # This program is free software; you can redistribute it and/or modify
309 # it under the terms of the GNU General Public License as published by
310@@ -323,6 +323,7 @@
311 source_branch, target_branch))
312 # We successfully created the target, remember it
313 if source_branch.get_push_location() is None or remember:
314+ # FIXME: Should be done only if we succeed ? -- vila 2012-01-18
315 source_branch.set_push_location(target_branch.base)
316 if not no_rebase:
317 old_last_revid = source_branch.last_revision()
318
319=== modified file 'bzrlib/plugins/launchpad/test_lp_open.py'
320--- bzrlib/plugins/launchpad/test_lp_open.py 2010-12-02 09:23:10 +0000
321+++ bzrlib/plugins/launchpad/test_lp_open.py 2012-02-14 17:36:32 +0000
322@@ -1,4 +1,4 @@
323-# Copyright (C) 2009, 2010 Canonical Ltd
324+# Copyright (C) 2009, 2010, 2012 Canonical Ltd
325 #
326 # This program is free software; you can redistribute it and/or modify
327 # it under the terms of the GNU General Public License as published by
328@@ -51,8 +51,7 @@
329
330 def test_launchpad_branch_with_public_location(self):
331 branch = self.make_branch('lp')
332- branch.set_public_branch(
333- 'bzr+ssh://bazaar.launchpad.net/~foo/bar/baz')
334+ branch.set_public_branch('bzr+ssh://bazaar.launchpad.net/~foo/bar/baz')
335 self.assertEqual(
336 ['Opening https://code.launchpad.net/~foo/bar/baz in web '
337 'browser'],
338@@ -60,10 +59,14 @@
339
340 def test_launchpad_branch_with_public_and_push_location(self):
341 branch = self.make_branch('lp')
342- branch.set_public_branch(
343- 'bzr+ssh://bazaar.launchpad.net/~foo/bar/public')
344- branch.set_push_location(
345- 'bzr+ssh://bazaar.launchpad.net/~foo/bar/push')
346+ branch.lock_write()
347+ try:
348+ branch.set_public_branch(
349+ 'bzr+ssh://bazaar.launchpad.net/~foo/bar/public')
350+ branch.set_push_location(
351+ 'bzr+ssh://bazaar.launchpad.net/~foo/bar/push')
352+ finally:
353+ branch.unlock()
354 self.assertEqual(
355 ['Opening https://code.launchpad.net/~foo/bar/public in web '
356 'browser'],
357@@ -73,8 +76,7 @@
358 # lp-open falls back to the push location if it cannot find a public
359 # location.
360 branch = self.make_branch('lp')
361- branch.set_push_location(
362- 'bzr+ssh://bazaar.launchpad.net/~foo/bar/baz')
363+ branch.set_push_location('bzr+ssh://bazaar.launchpad.net/~foo/bar/baz')
364 self.assertEqual(
365 ['Opening https://code.launchpad.net/~foo/bar/baz in web '
366 'browser'],
367
368=== modified file 'bzrlib/push.py'
369--- bzrlib/push.py 2012-01-19 17:23:00 +0000
370+++ bzrlib/push.py 2012-02-14 17:36:32 +0000
371@@ -1,4 +1,4 @@
372-# Copyright (C) 2008, 2009, 2010 Canonical Ltd
373+# Copyright (C) 2008-2012 Canonical Ltd
374 #
375 # This program is free software; you can redistribute it and/or modify
376 # it under the terms of the GNU General Public License as published by
377@@ -135,6 +135,7 @@
378 # Remembers if asked explicitly or no previous location is set
379 if (remember
380 or (remember is None and br_from.get_push_location() is None)):
381+ # FIXME: Should be done only if we succeed ? -- vila 2012-01-18
382 br_from.set_push_location(br_to.base)
383 else:
384 if stacked_on is not None:
385
386=== modified file 'bzrlib/remote.py'
387--- bzrlib/remote.py 2012-01-28 00:56:56 +0000
388+++ bzrlib/remote.py 2012-02-14 17:36:32 +0000
389@@ -1,4 +1,4 @@
390-# Copyright (C) 2006-2011 Canonical Ltd
391+# Copyright (C) 2006-2012 Canonical Ltd
392 #
393 # This program is free software; you can redistribute it and/or modify
394 # it under the terms of the GNU General Public License as published by
395@@ -3262,20 +3262,6 @@
396 self.id = "branch"
397 self._real_store = None
398
399- def lock_write(self, token=None):
400- return self.branch.lock_write(token)
401-
402- def unlock(self):
403- return self.branch.unlock()
404-
405- @needs_write_lock
406- def save(self):
407- # We need to be able to override the undecorated implementation
408- self.save_without_locking()
409-
410- def save_without_locking(self):
411- super(RemoteBranchStore, self).save()
412-
413 def external_url(self):
414 return self.branch.user_url
415
416@@ -3366,6 +3352,7 @@
417 self._repo_lock_token = None
418 self._lock_count = 0
419 self._leave_lock = False
420+ self.conf_store = None
421 # Setup a format: note that we cannot call _ensure_real until all the
422 # attributes above are set: This code cannot be moved higher up in this
423 # function.
424@@ -3414,7 +3401,9 @@
425 return RemoteBranchConfig(self)
426
427 def _get_config_store(self):
428- return RemoteBranchStore(self)
429+ if self.conf_store is None:
430+ self.conf_store = RemoteBranchStore(self)
431+ return self.conf_store
432
433 def _get_real_transport(self):
434 # if we try vfs access, return the real branch's vfs transport
435@@ -3440,6 +3429,10 @@
436 self.bzrdir._ensure_real()
437 self._real_branch = self.bzrdir._real_bzrdir.open_branch(
438 ignore_fallbacks=self._real_ignore_fallbacks, name=self._name)
439+ # The remote branch and the real branch shares the same store. If
440+ # we don't, there will always be cases where one of the stores
441+ # doesn't see an update made on the other.
442+ self._real_branch.conf_store = self.conf_store
443 if self.repository._real_repository is None:
444 # Give the remote repository the matching real repo.
445 real_repo = self._real_branch.repository
446@@ -3523,6 +3516,13 @@
447
448 def set_stacked_on_url(self, url):
449 branch.Branch.set_stacked_on_url(self, url)
450+ # We need the stacked_on_url to be visible both locally (to not query
451+ # it repeatedly) and remotely (so smart verbs can get it server side)
452+ # Without the following line,
453+ # bzrlib.tests.per_branch.test_create_clone.TestCreateClone
454+ # .test_create_clone_on_transport_stacked_hooks_get_stacked_branch
455+ # fails for remote branches -- vila 2012-01-04
456+ self.conf_store.save_changes()
457 if not url:
458 self._is_stacked = False
459 else:
460@@ -3656,6 +3656,8 @@
461 try:
462 self._lock_count -= 1
463 if not self._lock_count:
464+ if self.conf_store is not None:
465+ self.conf_store.save_changes()
466 self._clear_cached_state()
467 mode = self._lock_mode
468 self._lock_mode = None
469@@ -3951,8 +3953,7 @@
470 last_rev=last_rev,other_branch=other_branch))
471
472 def set_push_location(self, location):
473- self._ensure_real()
474- return self._real_branch.set_push_location(location)
475+ self._set_config_location('push_location', location)
476
477 def heads_to_fetch(self):
478 if self._format._use_default_local_heads_to_fetch():
479
480=== modified file 'bzrlib/switch.py'
481--- bzrlib/switch.py 2012-01-26 15:31:02 +0000
482+++ bzrlib/switch.py 2012-02-14 17:36:32 +0000
483@@ -1,4 +1,4 @@
484-# Copyright (C) 2007, 2009, 2010 Canonical Ltd.
485+# Copyright (C) 2007, 2009-2012 Canonical Ltd.
486 #
487 # This program is free software; you can redistribute it and/or modify
488 # it under the terms of the GNU General Public License as published by
489@@ -105,11 +105,15 @@
490 'Unable to connect to current master branch %(target)s: '
491 '%(error)s To switch anyway, use --force.') %
492 e.__dict__)
493- b.set_bound_location(None)
494- b.pull(to_branch, overwrite=True,
495- possible_transports=possible_transports)
496- b.set_bound_location(to_branch.base)
497- b.set_parent(b.get_master_branch().get_parent())
498+ b.lock_write()
499+ try:
500+ b.set_bound_location(None)
501+ b.pull(to_branch, overwrite=True,
502+ possible_transports=possible_transports)
503+ b.set_bound_location(to_branch.base)
504+ b.set_parent(b.get_master_branch().get_parent())
505+ finally:
506+ b.unlock()
507 else:
508 # If this is a standalone tree and the new branch
509 # is derived from this one, create a lightweight checkout.
510
511=== modified file 'bzrlib/tests/blackbox/test_bound_branches.py'
512--- bzrlib/tests/blackbox/test_bound_branches.py 2012-01-05 13:02:31 +0000
513+++ bzrlib/tests/blackbox/test_bound_branches.py 2012-02-14 17:36:32 +0000
514@@ -127,12 +127,13 @@
515
516 def test_double_binding(self):
517 child_tree = self.create_branches()[1]
518-
519- child2_tree = child_tree.bzrdir.sprout('child2').open_workingtree()
520+ child_tree.bzrdir.sprout('child2')
521
522 # Double binding succeeds, but committing to child2 should fail
523 self.run_bzr('bind ../child', working_dir='child2')
524
525+ # Refresh the child tree object as 'unbind' modified it
526+ child2_tree = bzrdir.BzrDir.open('child2').open_workingtree()
527 self.assertRaises(errors.CommitToDoubleBoundBranch,
528 child2_tree.commit, message='child2', allow_pointless=True)
529
530@@ -150,6 +151,8 @@
531 self.run_bzr("commit -m child", retcode=3, working_dir='child')
532 self.check_revno(1, 'child')
533 self.run_bzr('unbind', working_dir='child')
534+ # Refresh the child tree/branch objects as 'unbind' modified them
535+ child_tree = child_tree.bzrdir.open_workingtree()
536 child_tree.commit(message='child')
537 self.check_revno(2, 'child')
538
539@@ -200,6 +203,8 @@
540
541 self.run_bzr('unbind', working_dir='child')
542
543+ # Refresh the child tree/branch objects as 'unbind' modified them
544+ child_tree = child_tree.bzrdir.open_workingtree()
545 child_tree.commit(message='child', allow_pointless=True)
546 self.check_revno(2, 'child')
547
548@@ -210,6 +215,8 @@
549 # These branches have diverged, but bind should succeed anyway
550 self.run_bzr('bind ../base', working_dir='child')
551
552+ # Refresh the child tree/branch objects as 'bind' modified them
553+ child_tree = child_tree.bzrdir.open_workingtree()
554 # This should turn the local commit into a merge
555 child_tree.update()
556 child_tree.commit(message='merged')
557@@ -248,6 +255,8 @@
558 child_tree = self.create_branches()[1]
559
560 self.run_bzr('unbind', working_dir='child')
561+ # Refresh the child tree/branch objects as 'bind' modified them
562+ child_tree = child_tree.bzrdir.open_workingtree()
563 child_tree.commit(message='child', allow_pointless=True)
564 self.check_revno(2, 'child')
565 self.check_revno(1, 'base')
566
567=== modified file 'bzrlib/tests/blackbox/test_checkout.py'
568--- bzrlib/tests/blackbox/test_checkout.py 2012-01-20 21:33:24 +0000
569+++ bzrlib/tests/blackbox/test_checkout.py 2012-02-14 17:36:32 +0000
570@@ -223,5 +223,5 @@
571 # being too low. If rpc_count increases, more network roundtrips have
572 # become necessary for this use case. Please do not adjust this number
573 # upwards without agreement from bzr's network support maintainers.
574- self.assertLength(15, self.hpss_calls)
575+ self.assertLength(13, self.hpss_calls)
576 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
577
578=== modified file 'bzrlib/tests/blackbox/test_commit.py'
579--- bzrlib/tests/blackbox/test_commit.py 2012-02-07 00:49:58 +0000
580+++ bzrlib/tests/blackbox/test_commit.py 2012-02-14 17:36:32 +0000
581@@ -887,7 +887,7 @@
582 # being too low. If rpc_count increases, more network roundtrips have
583 # become necessary for this use case. Please do not adjust this number
584 # upwards without agreement from bzr's network support maintainers.
585- self.assertLength(214, self.hpss_calls)
586+ self.assertLength(211, self.hpss_calls)
587 self.assertLength(2, self.hpss_connections)
588 self.expectFailure("commit still uses VFS calls",
589 self.assertThat, self.hpss_calls, ContainsNoVfsCalls)
590
591=== modified file 'bzrlib/tests/blackbox/test_dpush.py'
592--- bzrlib/tests/blackbox/test_dpush.py 2011-09-22 12:13:12 +0000
593+++ bzrlib/tests/blackbox/test_dpush.py 2012-02-14 17:36:32 +0000
594@@ -1,4 +1,4 @@
595-# Copyright (C) 2009, 2010, 2011 Canonical Ltd
596+# Copyright (C) 2009-2012 Canonical Ltd
597 #
598 # This program is free software; you can redistribute it and/or modify
599 # it under the terms of the GNU General Public License as published by
600@@ -19,6 +19,7 @@
601
602
603 from bzrlib import (
604+ branch,
605 tests,
606 )
607 from bzrlib.tests import (
608@@ -144,10 +145,8 @@
609 'to', format=test_foreign.DummyForeignVcsDirFormat())
610
611 def set_config_push_strict(self, value):
612- # set config var (any of bazaar.conf, locations.conf, branch.conf
613- # should do)
614- conf = self.tree.branch.get_config_stack()
615- conf.set('dpush_strict', value)
616+ br = branch.Branch.open('local')
617+ br.get_config_stack().set('dpush_strict', value)
618
619 _default_command = ['dpush', '../to']
620
621
622=== modified file 'bzrlib/tests/blackbox/test_info.py'
623--- bzrlib/tests/blackbox/test_info.py 2012-01-26 15:36:39 +0000
624+++ bzrlib/tests/blackbox/test_info.py 2012-02-14 17:36:32 +0000
625@@ -1,4 +1,4 @@
626-# Copyright (C) 2006-2010 Canonical Ltd
627+# Copyright (C) 2006-2012 Canonical Ltd
628 #
629 # This program is free software; you can redistribute it and/or modify
630 # it under the terms of the GNU General Public License as published by
631@@ -1548,7 +1548,7 @@
632 # being too low. If rpc_count increases, more network roundtrips have
633 # become necessary for this use case. Please do not adjust this number
634 # upwards without agreement from bzr's network support maintainers.
635- self.assertLength(12, self.hpss_calls)
636+ self.assertLength(10, self.hpss_calls)
637 self.assertLength(1, self.hpss_connections)
638 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
639
640@@ -1565,6 +1565,6 @@
641 # being too low. If rpc_count increases, more network roundtrips have
642 # become necessary for this use case. Please do not adjust this number
643 # upwards without agreement from bzr's network support maintainers.
644- self.assertLength(16, self.hpss_calls)
645+ self.assertLength(14, self.hpss_calls)
646 self.assertLength(1, self.hpss_connections)
647 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
648
649=== modified file 'bzrlib/tests/blackbox/test_log.py'
650--- bzrlib/tests/blackbox/test_log.py 2011-12-21 14:50:43 +0000
651+++ bzrlib/tests/blackbox/test_log.py 2012-02-14 17:36:32 +0000
652@@ -1096,7 +1096,7 @@
653 # upwards without agreement from bzr's network support maintainers.
654 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
655 self.assertLength(1, self.hpss_connections)
656- self.assertLength(10, self.hpss_calls)
657+ self.assertLength(9, self.hpss_calls)
658
659 def test_verbose_log(self):
660 self.setup_smart_server_with_call_log()
661@@ -1111,7 +1111,7 @@
662 # being too low. If rpc_count increases, more network roundtrips have
663 # become necessary for this use case. Please do not adjust this number
664 # upwards without agreement from bzr's network support maintainers.
665- self.assertLength(11, self.hpss_calls)
666+ self.assertLength(10, self.hpss_calls)
667 self.assertLength(1, self.hpss_connections)
668 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
669
670@@ -1128,6 +1128,6 @@
671 # being too low. If rpc_count increases, more network roundtrips have
672 # become necessary for this use case. Please do not adjust this number
673 # upwards without agreement from bzr's network support maintainers.
674- self.assertLength(15, self.hpss_calls)
675+ self.assertLength(14, self.hpss_calls)
676 self.assertLength(1, self.hpss_connections)
677 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
678
679=== modified file 'bzrlib/tests/blackbox/test_merge.py'
680--- bzrlib/tests/blackbox/test_merge.py 2012-01-05 13:02:31 +0000
681+++ bzrlib/tests/blackbox/test_merge.py 2012-02-14 17:36:32 +0000
682@@ -278,6 +278,8 @@
683
684 base = urlutils.local_path_from_url(branch_a.base)
685 self.assertEndsWith(err, '+N b\nAll changes applied successfully.\n')
686+ # re-open branch as external run_bzr modified it
687+ branch_b = branch_b.bzrdir.open_branch()
688 self.assertEquals(osutils.abspath(branch_b.get_submit_branch()),
689 osutils.abspath(parent))
690 # test implicit --remember when committing new file
691@@ -295,6 +297,8 @@
692 working_dir='branch_b')
693 self.assertEquals(out, '')
694 self.assertEquals(err, '+N c\nAll changes applied successfully.\n')
695+ # re-open branch as external run_bzr modified it
696+ branch_b = branch_b.bzrdir.open_branch()
697 self.assertEquals(osutils.abspath(branch_b.get_submit_branch()),
698 osutils.abspath(branch_c.bzrdir.root_transport.base))
699 # re-open tree as external run_bzr modified it
700@@ -553,10 +557,16 @@
701 tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
702 tree_c = tree_a.bzrdir.sprout('c').open_workingtree()
703 out, err = self.run_bzr(['merge', '-d', 'c'])
704- self.assertContainsRe(err, 'Merging from remembered parent location .*a\/')
705- tree_c.branch.set_submit_branch(tree_b.bzrdir.root_transport.base)
706+ self.assertContainsRe(err,
707+ 'Merging from remembered parent location .*a\/')
708+ tree_c.branch.lock_write()
709+ try:
710+ tree_c.branch.set_submit_branch(tree_b.bzrdir.root_transport.base)
711+ finally:
712+ tree_c.branch.unlock()
713 out, err = self.run_bzr(['merge', '-d', 'c'])
714- self.assertContainsRe(err, 'Merging from remembered submit location .*b\/')
715+ self.assertContainsRe(err,
716+ 'Merging from remembered submit location .*b\/')
717
718 def test_remember_sets_submit(self):
719 tree_a = self.make_branch_and_tree('a')
720@@ -566,11 +576,13 @@
721
722 # Remember should not happen if using default from parent
723 out, err = self.run_bzr(['merge', '-d', 'b'])
724- self.assertIs(tree_b.branch.get_submit_branch(), None)
725+ refreshed = workingtree.WorkingTree.open('b')
726+ self.assertIs(refreshed.branch.get_submit_branch(), None)
727
728 # Remember should happen if user supplies location
729 out, err = self.run_bzr(['merge', '-d', 'b', 'a'])
730- self.assertEqual(tree_b.branch.get_submit_branch(),
731+ refreshed = workingtree.WorkingTree.open('b')
732+ self.assertEqual(refreshed.branch.get_submit_branch(),
733 tree_a.bzrdir.root_transport.base)
734
735 def test_no_remember_dont_set_submit(self):
736
737=== modified file 'bzrlib/tests/blackbox/test_merge_directive.py'
738--- bzrlib/tests/blackbox/test_merge_directive.py 2012-01-05 17:46:44 +0000
739+++ bzrlib/tests/blackbox/test_merge_directive.py 2012-02-14 17:36:32 +0000
740@@ -223,7 +223,8 @@
741
742 def test_mail_uses_config(self):
743 tree1, tree2 = self.prepare_merge_directive()
744- tree1.branch.get_config_stack().set('smtp_server', 'bogushost')
745+ br = tree1.branch
746+ br.get_config_stack().set('smtp_server', 'bogushost')
747 md_text, errr, connect_calls, sendmail_calls =\
748 self.run_bzr_fakemail('merge-directive --mail-to'
749 ' pqm@example.com --plain ../tree2 .')
750
751=== modified file 'bzrlib/tests/blackbox/test_pull.py'
752--- bzrlib/tests/blackbox/test_pull.py 2012-01-05 17:46:44 +0000
753+++ bzrlib/tests/blackbox/test_pull.py 2012-02-14 17:36:32 +0000
754@@ -236,6 +236,7 @@
755 tree_a.commit('commit b')
756 # reset parent
757 parent = branch_b.get_parent()
758+ branch_b = branch.Branch.open('branch_b')
759 branch_b.set_parent(None)
760 self.assertEqual(None, branch_b.get_parent())
761 # test pull for failure without parent set
762@@ -252,16 +253,22 @@
763 ('','bzr: ERROR: These branches have diverged.'
764 ' Use the missing command to see how.\n'
765 'Use the merge command to reconcile them.\n'))
766- self.assertEqual(branch_b.get_parent(), parent)
767+ tree_b = tree_b.bzrdir.open_workingtree()
768+ branch_b = tree_b.branch
769+ self.assertEqual(parent, branch_b.get_parent())
770 # test implicit --remember after resolving previous failure
771 uncommit.uncommit(branch=branch_b, tree=tree_b)
772 t.delete('branch_b/d')
773 self.run_bzr('pull', working_dir='branch_b')
774+ # Refresh the branch object as 'pull' modified it
775+ branch_b = branch_b.bzrdir.open_branch()
776 self.assertEqual(branch_b.get_parent(), parent)
777 # test explicit --remember
778 self.run_bzr('pull ../branch_c --remember', working_dir='branch_b')
779- self.assertEqual(branch_b.get_parent(),
780- branch_c.bzrdir.root_transport.base)
781+ # Refresh the branch object as 'pull' modified it
782+ branch_b = branch_b.bzrdir.open_branch()
783+ self.assertEqual(branch_c.bzrdir.root_transport.base,
784+ branch_b.get_parent())
785
786 def test_pull_bundle(self):
787 from bzrlib.testament import Testament
788@@ -360,8 +367,7 @@
789 def test_pull_verbose_uses_default_log(self):
790 tree = self.example_branch('source')
791 target = self.make_branch_and_tree('target')
792- target_config = target.branch.get_config_stack()
793- target_config.set('log_format', 'short')
794+ target.branch.get_config_stack().set('log_format', 'short')
795 out = self.run_bzr('pull -v source -d target')[0]
796 self.assertContainsRe(out, r'\n {4}1 .*\n {6}setup\n')
797 self.assertNotContainsRe(
798
799=== modified file 'bzrlib/tests/blackbox/test_push.py'
800--- bzrlib/tests/blackbox/test_push.py 2011-12-24 10:10:59 +0000
801+++ bzrlib/tests/blackbox/test_push.py 2012-02-14 17:36:32 +0000
802@@ -1,4 +1,4 @@
803-# Copyright (C) 2006-2011 Canonical Ltd
804+# Copyright (C) 2006-2012 Canonical Ltd
805 #
806 # This program is free software; you can redistribute it and/or modify
807 # it under the terms of the GNU General Public License as published by
808@@ -117,6 +117,8 @@
809 self.assertEquals(out,
810 ('','bzr: ERROR: These branches have diverged. '
811 'See "bzr help diverged-branches" for more information.\n'))
812+ # Refresh the branch as 'push' modified it
813+ branch_a = branch_a.bzrdir.open_branch()
814 self.assertEquals(osutils.abspath(branch_a.get_push_location()),
815 osutils.abspath(branch_b.bzrdir.root_transport.base))
816
817@@ -124,6 +126,8 @@
818 uncommit.uncommit(branch=branch_b, tree=tree_b)
819 transport.delete('branch_b/c')
820 out, err = self.run_bzr('push', working_dir='branch_a')
821+ # Refresh the branch as 'push' modified it
822+ branch_a = branch_a.bzrdir.open_branch()
823 path = branch_a.get_push_location()
824 self.assertEqual(err,
825 'Using saved push location: %s\n'
826@@ -134,6 +138,8 @@
827 branch_b.bzrdir.root_transport.base)
828 # test explicit --remember
829 self.run_bzr('push ../branch_c --remember', working_dir='branch_a')
830+ # Refresh the branch as 'push' modified it
831+ branch_a = branch_a.bzrdir.open_branch()
832 self.assertEquals(branch_a.get_push_location(),
833 branch_c.bzrdir.root_transport.base)
834
835@@ -176,11 +182,12 @@
836 t.add('file')
837 t.commit('commit 1')
838 self.run_bzr('push -d tree pushed-to')
839- path = t.branch.get_push_location()
840+ # Refresh the branch as 'push' modified it and get the push location
841+ push_loc = t.branch.bzrdir.open_branch().get_push_location()
842 out, err = self.run_bzr('push', working_dir="tree")
843 self.assertEqual('Using saved push location: %s\n'
844 'No new revisions or tags to push.\n' %
845- urlutils.local_path_from_url(path), err)
846+ urlutils.local_path_from_url(push_loc), err)
847 out, err = self.run_bzr('push -q', working_dir="tree")
848 self.assertEqual('', out)
849 self.assertEqual('', err)
850@@ -285,7 +292,7 @@
851 # being too low. If rpc_count increases, more network roundtrips have
852 # become necessary for this use case. Please do not adjust this number
853 # upwards without agreement from bzr's network support maintainers.
854- self.assertLength(14, self.hpss_calls)
855+ self.assertLength(15, self.hpss_calls)
856 self.assertLength(1, self.hpss_connections)
857 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
858 remote = branch.Branch.open('public')
859@@ -506,7 +513,8 @@
860 trunk_public = self.make_branch('public_trunk', format='1.9')
861 trunk_public.pull(trunk_tree.branch)
862 trunk_public_url = self.get_readonly_url('public_trunk')
863- trunk_tree.branch.set_public_branch(trunk_public_url)
864+ br = trunk_tree.branch
865+ br.set_public_branch(trunk_public_url)
866 # now we do a stacked push, which should determine the public location
867 # for us.
868 out, err = self.run_bzr(['push', '--stacked',
869@@ -703,10 +711,8 @@
870 self.tree.commit('modify file', rev_id='modified')
871
872 def set_config_push_strict(self, value):
873- # set config var (any of bazaar.conf, locations.conf, branch.conf
874- # should do)
875- conf = self.tree.branch.get_config_stack()
876- conf.set('push_strict', value)
877+ br = branch.Branch.open('local')
878+ br.get_config_stack().set('push_strict', value)
879
880 _default_command = ['push', '../to']
881 _default_wd = 'local'
882
883=== modified file 'bzrlib/tests/blackbox/test_reconfigure.py'
884--- bzrlib/tests/blackbox/test_reconfigure.py 2011-09-06 18:37:06 +0000
885+++ bzrlib/tests/blackbox/test_reconfigure.py 2012-02-14 17:36:32 +0000
886@@ -1,4 +1,4 @@
887-# Copyright (C) 2007, 2009 Canonical Ltd
888+# Copyright (C) 2007-2012 Canonical Ltd
889 #
890 # This program is free software; you can redistribute it and/or modify
891 # it under the terms of the GNU General Public License as published by
892@@ -242,20 +242,19 @@
893 branch_2 = tree_2.branch
894 # now reconfigure to be stacked
895 out, err = self.run_bzr('reconfigure --stacked-on b1 b2')
896- self.assertContainsRe(out,
897- '^.*/b2/ is now stacked on ../b1\n$')
898+ self.assertContainsRe(out, '^.*/b2/ is now stacked on ../b1\n$')
899 self.assertEquals('', err)
900 # can also give the absolute URL of the branch, and it gets stored
901 # as a relative path if possible
902 out, err = self.run_bzr('reconfigure --stacked-on %s b2'
903- % (self.get_url('b1'),))
904- self.assertContainsRe(out,
905- '^.*/b2/ is now stacked on ../b1\n$')
906+ % (self.get_url('b1'),))
907+ self.assertContainsRe(out, '^.*/b2/ is now stacked on ../b1\n$')
908 self.assertEquals('', err)
909+ # Refresh the branch as 'reconfigure' modified it
910+ branch_2 = branch_2.bzrdir.open_branch()
911 # It should be given a relative URL to the destination, if possible,
912 # because that's most likely to work across different transports
913- self.assertEquals(branch_2.get_stacked_on_url(),
914- '../b1')
915+ self.assertEquals('../b1', branch_2.get_stacked_on_url())
916 # commit, and it should be stored into b2's repo
917 self.build_tree_contents([('foo', 'new foo')])
918 tree_2.commit('update foo')
919@@ -264,8 +263,9 @@
920 self.assertContainsRe(out,
921 '^.*/b2/ is now not stacked\n$')
922 self.assertEquals('', err)
923- self.assertRaises(errors.NotStacked,
924- branch_2.get_stacked_on_url)
925+ # Refresh the branch as 'reconfigure' modified it
926+ branch_2 = branch_2.bzrdir.open_branch()
927+ self.assertRaises(errors.NotStacked, branch_2.get_stacked_on_url)
928
929 # XXX: Needs a test for reconfiguring stacking and shape at the same time;
930 # no branch at location; stacked-on is not a branch; quiet mode.
931
932=== modified file 'bzrlib/tests/blackbox/test_send.py'
933--- bzrlib/tests/blackbox/test_send.py 2012-02-01 19:55:27 +0000
934+++ bzrlib/tests/blackbox/test_send.py 2012-02-14 17:36:32 +0000
935@@ -1,4 +1,4 @@
936-# Copyright (C) 2006-2011 Canonical Ltd
937+# Copyright (C) 2006-2012 Canonical Ltd
938 # Authors: Aaron Bentley
939 #
940 # This program is free software; you can redistribute it and/or modify
941@@ -230,12 +230,13 @@
942 'send -f branch -o- --format=0.999')[0]
943
944 def test_format_child_option(self):
945- parent_config = branch.Branch.open('parent').get_config_stack()
946- parent_config.set('child_submit_format', '4')
947+ br = branch.Branch.open('parent')
948+ conf = br.get_config_stack()
949+ conf.set('child_submit_format', '4')
950 md = self.get_MD([])
951 self.assertIs(merge_directive.MergeDirective2, md.__class__)
952
953- parent_config.set('child_submit_format', '0.9')
954+ conf.set('child_submit_format', '0.9')
955 md = self.get_MD([])
956 self.assertFormatIs('# Bazaar revision bundle v0.9', md)
957
958@@ -243,7 +244,7 @@
959 self.assertFormatIs('# Bazaar revision bundle v0.9', md)
960 self.assertIs(merge_directive.MergeDirective, md.__class__)
961
962- parent_config.set('child_submit_format', '0.999')
963+ conf.set('child_submit_format', '0.999')
964 self.run_bzr_error(["No such send format '0.999'"],
965 'send -f branch -o-')[0]
966
967@@ -293,10 +294,8 @@
968 _default_additional_warning = 'Uncommitted changes will not be sent.'
969
970 def set_config_send_strict(self, value):
971- # set config var (any of bazaar.conf, locations.conf, branch.conf
972- # should do)
973- conf = self.local_tree.branch.get_config_stack()
974- conf.set('send_strict', value)
975+ br = branch.Branch.open('local')
976+ br.get_config_stack().set('send_strict', value)
977
978 def assertSendFails(self, args):
979 out, err = self.run_send(args, rc=3, err_re=self._default_errors)
980@@ -463,6 +462,6 @@
981 # being too low. If rpc_count increases, more network roundtrips have
982 # become necessary for this use case. Please do not adjust this number
983 # upwards without agreement from bzr's network support maintainers.
984- self.assertLength(9, self.hpss_calls)
985+ self.assertLength(7, self.hpss_calls)
986 self.assertLength(1, self.hpss_connections)
987 self.assertThat(self.hpss_calls, ContainsNoVfsCalls)
988
989=== modified file 'bzrlib/tests/blackbox/test_switch.py'
990--- bzrlib/tests/blackbox/test_switch.py 2012-01-19 12:36:22 +0000
991+++ bzrlib/tests/blackbox/test_switch.py 2012-02-14 17:36:32 +0000
992@@ -1,4 +1,4 @@
993-# Copyright (C) 2007-2010 Canonical Ltd
994+# Copyright (C) 2007-2012 Canonical Ltd
995 # -*- coding: utf-8 -*-
996 #
997 # This program is free software; you can redistribute it and/or modify
998@@ -153,8 +153,11 @@
999 branchb_id = tree2.commit('bar')
1000 checkout = tree1.branch.create_checkout('heavyco/a', lightweight=False)
1001 self.run_bzr(['switch', 'branchb'], working_dir='heavyco/a')
1002+ # Refresh checkout as 'switch' modified it
1003+ checkout = checkout.bzrdir.open_workingtree()
1004 self.assertEqual(branchb_id, checkout.last_revision())
1005- self.assertEqual(tree2.branch.base, checkout.branch.get_bound_location())
1006+ self.assertEqual(tree2.branch.base,
1007+ checkout.branch.get_bound_location())
1008
1009 def test_switch_finds_relative_unicode_branch(self):
1010 """Switch will find 'foo' relative to the branch the checkout is of."""
1011
1012=== modified file 'bzrlib/tests/blackbox/test_whoami.py'
1013--- bzrlib/tests/blackbox/test_whoami.py 2012-01-05 13:43:16 +0000
1014+++ bzrlib/tests/blackbox/test_whoami.py 2012-02-14 17:36:32 +0000
1015@@ -19,6 +19,7 @@
1016
1017 import bzrlib
1018 from bzrlib import (
1019+ branch,
1020 config,
1021 errors,
1022 tests,
1023@@ -50,11 +51,14 @@
1024 out = self.run_bzr("whoami --email 'foo <foo@example.com>'", 3)[0]
1025 self.assertEquals("", out)
1026
1027+ def set_branch_email(self, b, email):
1028+ b.get_config_stack().set('email', email)
1029+
1030 def test_whoami_branch(self):
1031 """branch specific user identity works."""
1032 wt = self.make_branch_and_tree('.')
1033 b = bzrlib.branch.Branch.open('.')
1034- b.get_config_stack().set('email', 'Branch Identity <branch@identi.ty>')
1035+ self.set_branch_email(b, 'Branch Identity <branch@identi.ty>')
1036 self.assertWhoAmI('Branch Identity <branch@identi.ty>')
1037 self.assertWhoAmI('branch@identi.ty', '--email')
1038
1039@@ -79,8 +83,7 @@
1040 """
1041 wt = self.make_branch_and_tree('.')
1042 b = bzrlib.branch.Branch.open('.')
1043- b.get_config_stack().set(
1044- 'email', u'Branch Identity \u20ac ' + '<branch@identi.ty>')
1045+ self.set_branch_email(b, u'Branch Identity \u20ac <branch@identi.ty>')
1046 self.assertWhoAmI('Branch Identity ? <branch@identi.ty>',
1047 encoding='ascii')
1048 self.assertWhoAmI('branch@identi.ty', '--email',
1049@@ -100,20 +103,20 @@
1050 self.overrideEnv('EMAIL', None)
1051 self.overrideEnv('BZR_EMAIL', None)
1052 # Also, make sure that it's not inferred from mailname.
1053- self.overrideAttr(config, '_auto_user_id',
1054- lambda: (None, None))
1055+ self.overrideAttr(config, '_auto_user_id', lambda: (None, None))
1056 out, err = self.run_bzr(['whoami'], 3)
1057 self.assertContainsRe(err, 'Unable to determine your name')
1058
1059 def test_whoami_directory(self):
1060 """Test --directory option."""
1061 wt = self.make_branch_and_tree('subdir')
1062- c = wt.branch.get_config_stack()
1063- c.set('email', 'Branch Identity <branch@identi.ty>')
1064+ self.set_branch_email(wt.branch, 'Branch Identity <branch@identi.ty>')
1065 self.assertWhoAmI('Branch Identity <branch@identi.ty>',
1066 '--directory', 'subdir')
1067 self.run_bzr(['whoami', '--directory', 'subdir', '--branch',
1068 'Changed Identity <changed@identi.ty>'])
1069+ # Refresh wt as 'whoami' modified it
1070+ wt = wt.bzrdir.open_workingtree()
1071 c = wt.branch.get_config_stack()
1072 self.assertEquals('Changed Identity <changed@identi.ty>',
1073 c.get('email'))
1074@@ -121,8 +124,7 @@
1075 def test_whoami_remote_directory(self):
1076 """Test --directory option with a remote directory."""
1077 wt = self.make_branch_and_tree('subdir')
1078- c = wt.branch.get_config_stack()
1079- c.set('email', 'Branch Identity <branch@identi.ty>')
1080+ self.set_branch_email(wt.branch, 'Branch Identity <branch@identi.ty>')
1081 url = self.get_readonly_url() + '/subdir'
1082 self.assertWhoAmI('Branch Identity <branch@identi.ty>',
1083 '--directory', url)
1084@@ -131,7 +133,7 @@
1085 'Changed Identity <changed@identi.ty>'])
1086 # The identity has been set in the branch config (but not the global
1087 # config)
1088- c = wt.branch.get_config_stack()
1089+ c = branch.Branch.open(url).get_config_stack()
1090 self.assertEquals('Changed Identity <changed@identi.ty>',
1091 c.get('email'))
1092 # Ensuring that the value does not come from the bazaar.conf file
1093
1094=== modified file 'bzrlib/tests/per_branch/test_branch.py'
1095--- bzrlib/tests/per_branch/test_branch.py 2012-02-06 11:36:02 +0000
1096+++ bzrlib/tests/per_branch/test_branch.py 2012-02-14 17:36:32 +0000
1097@@ -1,4 +1,4 @@
1098-# Copyright (C) 2005-2011 Canonical Ltd
1099+# Copyright (C) 2005-2012 Canonical Ltd
1100 #
1101 # This program is free software; you can redistribute it and/or modify
1102 # it under the terms of the GNU General Public License as published by
1103@@ -779,22 +779,6 @@
1104 branch.unbind()
1105 self.assertEqual(None, branch.get_master_branch())
1106
1107- def test_unlocked_does_not_cache_master_branch(self):
1108- """Unlocked branches do not cache the result of get_master_branch."""
1109- master = self.make_branch('master')
1110- branch1 = self.make_branch('branch')
1111- try:
1112- branch1.bind(master)
1113- except errors.UpgradeRequired:
1114- raise tests.TestNotApplicable('Format does not support binding')
1115- # Open branch1 again
1116- branch2 = branch1.bzrdir.open_branch()
1117- self.assertNotEqual(None, branch1.get_master_branch())
1118- # Unbind the branch via branch2. branch1 isn't locked so will
1119- # immediately return the new value for get_master_branch.
1120- branch2.unbind()
1121- self.assertEqual(None, branch1.get_master_branch())
1122-
1123 def test_bind_clears_cached_master_branch(self):
1124 """b.bind clears any cached value of b.get_master_branch."""
1125 master1 = self.make_branch('master1')
1126
1127=== modified file 'bzrlib/tests/per_branch/test_break_lock.py'
1128--- bzrlib/tests/per_branch/test_break_lock.py 2011-09-30 13:35:45 +0000
1129+++ bzrlib/tests/per_branch/test_break_lock.py 2012-02-14 17:36:32 +0000
1130@@ -1,4 +1,4 @@
1131-# Copyright (C) 2006-2010 Canonical Ltd
1132+# Copyright (C) 2006-2012 Canonical Ltd
1133 #
1134 # This program is free software; you can redistribute it and/or modify
1135 # it under the terms of the GNU General Public License as published by
1136@@ -86,7 +86,8 @@
1137 master.lock_write()
1138 ui.ui_factory = ui.CannedInputUIFactory([True, True])
1139 try:
1140- self.unused_branch.break_lock()
1141+ fresh = _mod_branch.Branch.open(self.unused_branch.base)
1142+ fresh.break_lock()
1143 except NotImplementedError:
1144 # branch does not support break_lock
1145 master.unlock()
1146
1147=== modified file 'bzrlib/tests/per_branch/test_create_clone.py'
1148--- bzrlib/tests/per_branch/test_create_clone.py 2011-10-04 13:29:25 +0000
1149+++ bzrlib/tests/per_branch/test_create_clone.py 2012-02-14 17:36:32 +0000
1150@@ -1,4 +1,4 @@
1151-# Copyright (C) 2009, 2010 Canonical Ltd
1152+# Copyright (C) 2009-2012 Canonical Ltd
1153 #
1154 # This program is free software; you can redistribute it and/or modify
1155 # it under the terms of the GNU General Public License as published by
1156@@ -133,7 +133,6 @@
1157 trunk = tree.branch.create_clone_on_transport(
1158 self.get_transport('trunk'))
1159 revid = tree.commit('a second commit')
1160- source = tree.branch
1161 target_transport = self.get_transport('target')
1162 self.hook_calls = []
1163 branch.Branch.hooks.install_named_hook(
1164
1165=== modified file 'bzrlib/tests/test_branch.py'
1166--- bzrlib/tests/test_branch.py 2012-01-18 17:47:06 +0000
1167+++ bzrlib/tests/test_branch.py 2012-02-14 17:36:32 +0000
1168@@ -1,4 +1,4 @@
1169-# Copyright (C) 2006-2011 Canonical Ltd
1170+# Copyright (C) 2006-2012 Canonical Ltd
1171 #
1172 # This program is free software; you can redistribute it and/or modify
1173 # it under the terms of the GNU General Public License as published by
1174@@ -356,8 +356,8 @@
1175 self.assertPathDoesNotExist('a/.bzr/branch/parent')
1176 self.assertEqual('http://example.com', branch.get_parent())
1177 branch.set_push_location('sftp://example.com')
1178- config = branch.get_config_stack()
1179- self.assertEqual('sftp://example.com', config.get('push_location'))
1180+ conf = branch.get_config_stack()
1181+ self.assertEqual('sftp://example.com', conf.get('push_location'))
1182 branch.set_bound_location('ftp://example.com')
1183 self.assertPathDoesNotExist('a/.bzr/branch/bound')
1184 self.assertEqual('ftp://example.com', branch.get_bound_location())
1185
1186=== modified file 'bzrlib/tests/test_config.py'
1187--- bzrlib/tests/test_config.py 2012-02-03 10:28:47 +0000
1188+++ bzrlib/tests/test_config.py 2012-02-14 17:36:32 +0000
1189@@ -1,4 +1,4 @@
1190-# Copyright (C) 2005-2011 Canonical Ltd
1191+# Copyright (C) 2005-2012 Canonical Ltd
1192 #
1193 # This program is free software; you can redistribute it and/or modify
1194 # it under the terms of the GNU General Public License as published by
1195@@ -2924,6 +2924,11 @@
1196 def test_save_emptied_succeeds(self):
1197 store = self.get_store(self)
1198 store._load_from_string('foo=bar\n')
1199+ # FIXME: There should be a better way than relying on the test
1200+ # parametrization to identify branch.conf -- vila 2011-0526
1201+ if self.store_id in ('branch', 'remote_branch'):
1202+ # branch stores requires write locked branches
1203+ self.addCleanup(store.branch.lock_write().unlock)
1204 section = store.get_mutable_section(None)
1205 section.remove('foo')
1206 store.save()
1207@@ -2950,6 +2955,11 @@
1208
1209 def test_set_option_in_empty_store(self):
1210 store = self.get_store(self)
1211+ # FIXME: There should be a better way than relying on the test
1212+ # parametrization to identify branch.conf -- vila 2011-0526
1213+ if self.store_id in ('branch', 'remote_branch'):
1214+ # branch stores requires write locked branches
1215+ self.addCleanup(store.branch.lock_write().unlock)
1216 section = store.get_mutable_section(None)
1217 section.set('foo', 'bar')
1218 store.save()
1219@@ -2961,6 +2971,11 @@
1220 def test_set_option_in_default_section(self):
1221 store = self.get_store(self)
1222 store._load_from_string('')
1223+ # FIXME: There should be a better way than relying on the test
1224+ # parametrization to identify branch.conf -- vila 2011-0526
1225+ if self.store_id in ('branch', 'remote_branch'):
1226+ # branch stores requires write locked branches
1227+ self.addCleanup(store.branch.lock_write().unlock)
1228 section = store.get_mutable_section(None)
1229 section.set('foo', 'bar')
1230 store.save()
1231@@ -2972,6 +2987,11 @@
1232 def test_set_option_in_named_section(self):
1233 store = self.get_store(self)
1234 store._load_from_string('')
1235+ # FIXME: There should be a better way than relying on the test
1236+ # parametrization to identify branch.conf -- vila 2011-0526
1237+ if self.store_id in ('branch', 'remote_branch'):
1238+ # branch stores requires write locked branches
1239+ self.addCleanup(store.branch.lock_write().unlock)
1240 section = store.get_mutable_section('baz')
1241 section.set('foo', 'bar')
1242 store.save()
1243@@ -2981,8 +3001,13 @@
1244 self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
1245
1246 def test_load_hook(self):
1247- # We first needs to ensure that the store exists
1248+ # First, we need to ensure that the store exists
1249 store = self.get_store(self)
1250+ # FIXME: There should be a better way than relying on the test
1251+ # parametrization to identify branch.conf -- vila 2011-0526
1252+ if self.store_id in ('branch', 'remote_branch'):
1253+ # branch stores requires write locked branches
1254+ self.addCleanup(store.branch.lock_write().unlock)
1255 section = store.get_mutable_section('baz')
1256 section.set('foo', 'bar')
1257 store.save()
1258@@ -3004,6 +3029,11 @@
1259 config.ConfigHooks.install_named_hook('save', hook, None)
1260 self.assertLength(0, calls)
1261 store = self.get_store(self)
1262+ # FIXME: There should be a better way than relying on the test
1263+ # parametrization to identify branch.conf -- vila 2011-0526
1264+ if self.store_id in ('branch', 'remote_branch'):
1265+ # branch stores requires write locked branches
1266+ self.addCleanup(store.branch.lock_write().unlock)
1267 section = store.get_mutable_section('baz')
1268 section.set('foo', 'bar')
1269 store.save()
1270@@ -3835,7 +3865,8 @@
1271 super(TestStackExpandOptions, self).setUp()
1272 self.overrideAttr(config, 'option_registry', config.OptionRegistry())
1273 self.registry = config.option_registry
1274- self.conf = build_branch_stack(self)
1275+ store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
1276+ self.conf = config.Stack([store.get_sections], store)
1277
1278 def assertExpansion(self, expected, string, env=None):
1279 self.assertEquals(expected, self.conf.expand_options(string, env))
1280
1281=== modified file 'bzrlib/tests/test_directory_service.py'
1282--- bzrlib/tests/test_directory_service.py 2011-11-29 01:35:03 +0000
1283+++ bzrlib/tests/test_directory_service.py 2012-02-14 17:36:32 +0000
1284@@ -1,4 +1,4 @@
1285-# Copyright (C) 2008, 2009, 2010 Canonical Ltd
1286+# Copyright (C) 2008-2012 Canonical Ltd
1287 #
1288 # This program is free software; you can redistribute it and/or modify
1289 # it under the terms of the GNU General Public License as published by
1290@@ -65,55 +65,53 @@
1291
1292 class TestAliasDirectory(TestCaseWithTransport):
1293
1294+ def setUp(self):
1295+ super(TestAliasDirectory, self).setUp()
1296+ self.branch = self.make_branch('.')
1297+
1298+ def assertAliasFromBranch(self, setter, value, alias):
1299+ setter(value)
1300+ self.assertEquals(value, directories.dereference(alias))
1301+
1302 def test_lookup_parent(self):
1303- branch = self.make_branch('.')
1304- branch.set_parent('http://a')
1305- self.assertEqual('http://a', directories.dereference(':parent'))
1306+ self.assertAliasFromBranch(self.branch.set_parent, 'http://a',
1307+ ':parent')
1308
1309 def test_lookup_submit(self):
1310- branch = self.make_branch('.')
1311- branch.set_submit_branch('http://b')
1312- self.assertEqual('http://b', directories.dereference(':submit'))
1313+ self.assertAliasFromBranch(self.branch.set_submit_branch, 'http://b',
1314+ ':submit')
1315
1316 def test_lookup_public(self):
1317- branch = self.make_branch('.')
1318- branch.set_public_branch('http://c')
1319- self.assertEqual('http://c', directories.dereference(':public'))
1320+ self.assertAliasFromBranch(self.branch.set_public_branch, 'http://c',
1321+ ':public')
1322
1323 def test_lookup_bound(self):
1324- branch = self.make_branch('.')
1325- branch.set_bound_location('http://d')
1326- self.assertEqual('http://d', directories.dereference(':bound'))
1327+ self.assertAliasFromBranch(self.branch.set_bound_location, 'http://d',
1328+ ':bound')
1329
1330 def test_lookup_push(self):
1331- branch = self.make_branch('.')
1332- branch.set_push_location('http://e')
1333- self.assertEqual('http://e', directories.dereference(':push'))
1334+ self.assertAliasFromBranch(self.branch.set_push_location, 'http://e',
1335+ ':push')
1336
1337 def test_lookup_this(self):
1338- branch = self.make_branch('.')
1339- self.assertEqual(branch.base, directories.dereference(':this'))
1340+ self.assertEqual(self.branch.base, directories.dereference(':this'))
1341
1342 def test_extra_path(self):
1343- branch = self.make_branch('.')
1344- self.assertEqual(urlutils.join(branch.base, 'arg'),
1345+ self.assertEqual(urlutils.join(self.branch.base, 'arg'),
1346 directories.dereference(':this/arg'))
1347
1348 def test_lookup_badname(self):
1349- branch = self.make_branch('.')
1350 e = self.assertRaises(errors.InvalidLocationAlias,
1351 directories.dereference, ':booga')
1352 self.assertEqual('":booga" is not a valid location alias.',
1353 str(e))
1354
1355 def test_lookup_badvalue(self):
1356- branch = self.make_branch('.')
1357 e = self.assertRaises(errors.UnsetLocationAlias,
1358 directories.dereference, ':parent')
1359 self.assertEqual('No parent location assigned.', str(e))
1360
1361 def test_register_location_alias(self):
1362- branch = self.make_branch('.')
1363 self.addCleanup(AliasDirectory.branch_aliases.remove, "booga")
1364 AliasDirectory.branch_aliases.register("booga",
1365 lambda b: "UHH?", help="Nobody knows")
1366
1367=== modified file 'bzrlib/tests/test_info.py'
1368--- bzrlib/tests/test_info.py 2012-01-19 16:23:53 +0000
1369+++ bzrlib/tests/test_info.py 2012-02-14 17:36:32 +0000
1370@@ -1,4 +1,4 @@
1371-# Copyright (C) 2007-2011 Canonical Ltd
1372+# Copyright (C) 2007-2012 Canonical Ltd
1373 #
1374 # This program is free software; you can redistribute it and/or modify
1375 # it under the terms of the GNU General Public License as published by
1376@@ -322,10 +322,14 @@
1377
1378 def test_gather_related_braches(self):
1379 branch = self.make_branch('.')
1380- branch.set_public_branch('baz')
1381- branch.set_push_location('bar')
1382- branch.set_parent('foo')
1383- branch.set_submit_branch('qux')
1384+ branch.lock_write()
1385+ try:
1386+ branch.set_public_branch('baz')
1387+ branch.set_push_location('bar')
1388+ branch.set_parent('foo')
1389+ branch.set_submit_branch('qux')
1390+ finally:
1391+ branch.unlock()
1392 self.assertEqual(
1393 [('public branch', 'baz'), ('push branch', 'bar'),
1394 ('parent branch', 'foo'), ('submit branch', 'qux')],
1395
1396=== modified file 'bzrlib/tests/test_reconfigure.py'
1397--- bzrlib/tests/test_reconfigure.py 2011-12-05 17:31:43 +0000
1398+++ bzrlib/tests/test_reconfigure.py 2012-02-14 17:36:32 +0000
1399@@ -1,4 +1,4 @@
1400-# Copyright (C) 2007, 2008, 2009 Canonical Ltd
1401+# Copyright (C) 2007, 2008, 2009, 2011, 2012 Canonical Ltd
1402 #
1403 # This program is free software; you can redistribute it and/or modify
1404 # it under the terms of the GNU General Public License as published by
1405@@ -72,7 +72,8 @@
1406 checkout = branch.create_checkout('checkout')
1407 reconfiguration = reconfigure.Reconfigure.to_branch(checkout.bzrdir)
1408 reconfiguration.apply()
1409- self.assertIs(None, checkout.branch.get_bound_location())
1410+ reconfigured = bzrdir.BzrDir.open('checkout').open_branch()
1411+ self.assertIs(None, reconfigured.get_bound_location())
1412
1413 def prepare_lightweight_checkout_to_branch(self):
1414 branch = self.make_branch('branch')
1415@@ -147,16 +148,24 @@
1416 self.assertRaises(errors.NoBindLocation,
1417 reconfiguration._select_bind_location)
1418 branch.set_parent('http://parent')
1419+ reconfiguration = reconfigure.Reconfigure(branch.bzrdir)
1420 self.assertEqual('http://parent',
1421 reconfiguration._select_bind_location())
1422 branch.set_push_location('sftp://push')
1423+ reconfiguration = reconfigure.Reconfigure(branch.bzrdir)
1424 self.assertEqual('sftp://push',
1425 reconfiguration._select_bind_location())
1426- branch.set_bound_location('bzr://foo/old-bound')
1427- branch.set_bound_location(None)
1428+ branch.lock_write()
1429+ try:
1430+ branch.set_bound_location('bzr://foo/old-bound')
1431+ branch.set_bound_location(None)
1432+ finally:
1433+ branch.unlock()
1434+ reconfiguration = reconfigure.Reconfigure(branch.bzrdir)
1435 self.assertEqual('bzr://foo/old-bound',
1436 reconfiguration._select_bind_location())
1437 branch.set_bound_location('bzr://foo/cur-bound')
1438+ reconfiguration = reconfigure.Reconfigure(branch.bzrdir)
1439 self.assertEqual('bzr://foo/cur-bound',
1440 reconfiguration._select_bind_location())
1441 reconfiguration.new_bound_location = 'ftp://user-specified'
1442@@ -180,6 +189,7 @@
1443 self.assertRaises(errors.NoBindLocation, reconfiguration.apply)
1444 # setting a parent allows it to become a checkout
1445 tree.branch.set_parent(parent.base)
1446+ reconfiguration = reconfigure.Reconfigure.to_checkout(tree.bzrdir)
1447 reconfiguration.apply()
1448 # supplying a location allows it to become a checkout
1449 tree2 = self.make_branch_and_tree('tree2')
1450@@ -198,6 +208,8 @@
1451 self.assertRaises(errors.NoBindLocation, reconfiguration.apply)
1452 # setting a parent allows it to become a checkout
1453 tree.branch.set_parent(parent.base)
1454+ reconfiguration = reconfigure.Reconfigure.to_lightweight_checkout(
1455+ tree.bzrdir)
1456 reconfiguration.apply()
1457 # supplying a location allows it to become a checkout
1458 tree2 = self.make_branch_and_tree('tree2')
1459
1460=== modified file 'bzrlib/tests/test_remote.py'
1461--- bzrlib/tests/test_remote.py 2012-01-28 01:43:42 +0000
1462+++ bzrlib/tests/test_remote.py 2012-02-14 17:36:32 +0000
1463@@ -1,4 +1,4 @@
1464-# Copyright (C) 2006-2011 Canonical Ltd
1465+# Copyright (C) 2006-2012 Canonical Ltd
1466 #
1467 # This program is free software; you can redistribute it and/or modify
1468 # it under the terms of the GNU General Public License as published by
1469@@ -1296,7 +1296,7 @@
1470 verb = 'Branch.set_parent_location'
1471 self.disable_verb(verb)
1472 branch.set_parent('http://foo/')
1473- self.assertLength(13, self.hpss_calls)
1474+ self.assertLength(14, self.hpss_calls)
1475
1476
1477 class TestBranchGetTagsBytes(RemoteBranchTestCase):
1478@@ -1455,34 +1455,31 @@
1479 return branch
1480
1481 def test_backwards_compatible(self):
1482- branch = self.make_branch_with_tags()
1483- c = branch.get_config_stack()
1484- c.set('branch.fetch_tags', True)
1485- self.addCleanup(branch.lock_read().unlock)
1486+ br = self.make_branch_with_tags()
1487+ br.get_config_stack().set('branch.fetch_tags', True)
1488+ self.addCleanup(br.lock_read().unlock)
1489 # Disable the heads_to_fetch verb
1490 verb = 'Branch.heads_to_fetch'
1491 self.disable_verb(verb)
1492 self.reset_smart_call_log()
1493- result = branch.heads_to_fetch()
1494+ result = br.heads_to_fetch()
1495 self.assertEqual((set(['tip']), set(['rev-1', 'rev-2'])), result)
1496 self.assertEqual(
1497- ['Branch.last_revision_info', 'Branch.get_config_file',
1498- 'Branch.get_tags_bytes'],
1499+ ['Branch.last_revision_info', 'Branch.get_tags_bytes'],
1500 [call.call.method for call in self.hpss_calls])
1501
1502 def test_backwards_compatible_no_tags(self):
1503- branch = self.make_branch_with_tags()
1504- c = branch.get_config_stack()
1505- c.set('branch.fetch_tags', False)
1506- self.addCleanup(branch.lock_read().unlock)
1507+ br = self.make_branch_with_tags()
1508+ br.get_config_stack().set('branch.fetch_tags', False)
1509+ self.addCleanup(br.lock_read().unlock)
1510 # Disable the heads_to_fetch verb
1511 verb = 'Branch.heads_to_fetch'
1512 self.disable_verb(verb)
1513 self.reset_smart_call_log()
1514- result = branch.heads_to_fetch()
1515+ result = br.heads_to_fetch()
1516 self.assertEqual((set(['tip']), set()), result)
1517 self.assertEqual(
1518- ['Branch.last_revision_info', 'Branch.get_config_file'],
1519+ ['Branch.last_revision_info'],
1520 [call.call.method for call in self.hpss_calls])
1521
1522
1523@@ -2073,6 +2070,9 @@
1524 'Branch.get_config_file', ('memory:///', ),
1525 'success', ('ok', ), "# line 1\n")
1526 client.add_expected_call(
1527+ 'Branch.get_config_file', ('memory:///', ),
1528+ 'success', ('ok', ), "# line 1\n")
1529+ client.add_expected_call(
1530 'Branch.put_config_file', ('memory:///', 'branch token',
1531 'repo token'),
1532 'success', ('ok',))
1533@@ -2090,6 +2090,7 @@
1534 [('call', 'Branch.get_stacked_on_url', ('memory:///',)),
1535 ('call', 'Branch.lock_write', ('memory:///', '', '')),
1536 ('call_expecting_body', 'Branch.get_config_file', ('memory:///',)),
1537+ ('call_expecting_body', 'Branch.get_config_file', ('memory:///',)),
1538 ('call_with_body_bytes_expecting_body', 'Branch.put_config_file',
1539 ('memory:///', 'branch token', 'repo token'),
1540 '# line 1\nemail = The Dude <lebowski@example.com>\n'),
1541
1542=== modified file 'bzrlib/tests/test_smart.py'
1543--- bzrlib/tests/test_smart.py 2012-01-30 14:18:22 +0000
1544+++ bzrlib/tests/test_smart.py 2012-02-14 17:36:32 +0000
1545@@ -1,4 +1,4 @@
1546-# Copyright (C) 2006-2011 Canonical Ltd
1547+# Copyright (C) 2006-2012 Canonical Ltd
1548 #
1549 # This program is free software; you can redistribute it and/or modify
1550 # it under the terms of the GNU General Public License as published by
1551@@ -1323,6 +1323,8 @@
1552 finally:
1553 branch.unlock()
1554 self.assertEqual(smart_req.SuccessfulSmartServerResponse(()), response)
1555+ # Refresh branch as SetParentLocation modified it
1556+ branch = branch.bzrdir.open_branch()
1557 self.assertEqual(None, branch.get_parent())
1558
1559 def test_set_parent_something(self):
1560@@ -1332,11 +1334,12 @@
1561 branch_token, repo_token = self.get_lock_tokens(branch)
1562 try:
1563 response = request.execute('base', branch_token, repo_token,
1564- 'http://bar/')
1565+ 'http://bar/')
1566 finally:
1567 branch.unlock()
1568 self.assertEqual(smart_req.SuccessfulSmartServerResponse(()), response)
1569- self.assertEqual('http://bar/', branch.get_parent())
1570+ refreshed = _mod_branch.Branch.open(branch.base)
1571+ self.assertEqual('http://bar/', refreshed.get_parent())
1572
1573
1574 class TestSmartServerBranchRequestGetTagsBytes(