Merge lp:~mbp/bzr/components into lp:bzr

Proposed by Martin Pool
Status: Merged
Approved by: bzr PQM
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~mbp/bzr/components
Merge into: lp:bzr
Diff against target: 846 lines (+215/-94)
20 files modified
NEWS (+8/-0)
bzrlib/branch.py (+32/-34)
bzrlib/builtins.py (+1/-3)
bzrlib/bzrdir.py (+57/-14)
bzrlib/check.py (+3/-3)
bzrlib/errors.py (+3/-3)
bzrlib/info.py (+6/-7)
bzrlib/reconcile.py (+1/-1)
bzrlib/reconfigure.py (+5/-6)
bzrlib/remote.py (+17/-4)
bzrlib/repofmt/weaverepo.py (+1/-1)
bzrlib/repository.py (+14/-7)
bzrlib/smart/bzrdir.py (+1/-1)
bzrlib/tests/per_branch/test_branch.py (+13/-0)
bzrlib/tests/per_bzrdir/test_bzrdir.py (+12/-0)
bzrlib/tests/per_repository/test_repository.py (+15/-0)
bzrlib/tests/per_workingtree/test_workingtree.py (+13/-0)
bzrlib/tests/test_foreign.py (+1/-0)
bzrlib/upgrade.py (+2/-3)
bzrlib/workingtree.py (+10/-7)
To merge this branch: bzr merge lp:~mbp/bzr/components
Reviewer Review Type Date Requested Status
Vincent Ladeuil Approve
Andrew Bennetts Approve
Review via email: mp+23827@code.launchpad.net

Commit message

(mbp) add ControlComponent base class

Description of the change

This adds a consistent interface for getting the control_transport and url (ie ends in .bzr/...) and the user_transport and url (doesn't) across branch, repository and bzrdir. At the moment there is some undesirable random variation across classes, and the names are not very obvious. This is a root cause behind us sometimes showing users urls containing .bzr when we don't really mean too.

I think those names are pretty clear but better suggestions are welcome.

As a followon I need to do this for wts but I thought I'd send this up to start with.

To post a comment you must log in.
Revision history for this message
Andrew Bennetts (spiv) wrote :

I like this! The new names are much much clearer. It seems a bit of a shame to create another mixin, but the new code is certainly much nicer.

review: Approve
Revision history for this message
Andrew Bennetts (spiv) wrote :

I know you want to do workingtrees before you land this, but also don't forget to write a NEWS entry.

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

Sounds good, please land.

review: Approve
Revision history for this message
bzr PQM (bzr-pqm) wrote :

Successful steps
Failure output:
All lines of log output:
Executing star-merge http://bazaar.launchpad.net/~mbp/bzr/components at Tue Apr 27 09:01:40 2010
['Conflicts during merge: ', 'Text conflict in NEWS']

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'NEWS'
2--- NEWS 2010-04-27 18:09:12 +0000
3+++ NEWS 2010-04-28 07:06:33 +0000
4@@ -64,6 +64,14 @@
5 API Changes
6 ***********
7
8+* `BzrDir`, `Branch`, `Repository` and `WorkingTree` now all support `user_url`,
9+ `user_transport`, `control_url` and `control_transport` members pointing
10+ respectively to the directory containing the ``.bzr`` control directory,
11+ and to the directory within ``.bzr`` used for the particular component.
12+ All of them inherit from `ControlComponent` which provides default
13+ implementations.
14+ (Martin Pool)
15+
16 Internals
17 *********
18
19
20=== modified file 'bzrlib/branch.py'
21--- bzrlib/branch.py 2010-04-23 07:15:23 +0000
22+++ bzrlib/branch.py 2010-04-28 07:06:33 +0000
23@@ -63,23 +63,12 @@
24 BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
25
26
27-# TODO: Maybe include checks for common corruption of newlines, etc?
28-
29-# TODO: Some operations like log might retrieve the same revisions
30-# repeatedly to calculate deltas. We could perhaps have a weakref
31-# cache in memory to make this faster. In general anything can be
32-# cached in memory between lock and unlock operations. .. nb thats
33-# what the transaction identity map provides
34-
35-
36-######################################################################
37-# branch objects
38-
39-class Branch(object):
40+class Branch(bzrdir.ControlComponent):
41 """Branch holding a history of revisions.
42
43- base
44- Base directory/url of the branch.
45+ :ivar base:
46+ Base directory/url of the branch; using control_url and
47+ control_transport is more standardized.
48
49 hooks: An instance of BranchHooks.
50 """
51@@ -87,6 +76,14 @@
52 # - RBC 20060112
53 base = None
54
55+ @property
56+ def control_transport(self):
57+ return self._transport
58+
59+ @property
60+ def user_transport(self):
61+ return self.bzrdir.user_transport
62+
63 def __init__(self, *ignored, **ignored_too):
64 self.tags = self._format.make_tags(self)
65 self._revision_history_cache = None
66@@ -107,7 +104,7 @@
67 """Activate the branch/repository from url as a fallback repository."""
68 repo = self._get_fallback_repository(url)
69 if repo.has_same_location(self.repository):
70- raise errors.UnstackableLocationError(self.base, url)
71+ raise errors.UnstackableLocationError(self.user_url, url)
72 self.repository.add_fallback_repository(repo)
73
74 def break_lock(self):
75@@ -594,11 +591,11 @@
76 :param other: The branch to bind to
77 :type other: Branch
78 """
79- raise errors.UpgradeRequired(self.base)
80+ raise errors.UpgradeRequired(self.user_url)
81
82 def set_append_revisions_only(self, enabled):
83 if not self._format.supports_set_append_revisions_only():
84- raise errors.UpgradeRequired(self.base)
85+ raise errors.UpgradeRequired(self.user_url)
86 if enabled:
87 value = 'True'
88 else:
89@@ -652,7 +649,7 @@
90 def get_old_bound_location(self):
91 """Return the URL of the branch we used to be bound to
92 """
93- raise errors.UpgradeRequired(self.base)
94+ raise errors.UpgradeRequired(self.user_url)
95
96 def get_commit_builder(self, parents, config=None, timestamp=None,
97 timezone=None, committer=None, revprops=None,
98@@ -736,7 +733,7 @@
99 stacking.
100 """
101 if not self._format.supports_stacking():
102- raise errors.UnstackableBranchFormat(self._format, self.base)
103+ raise errors.UnstackableBranchFormat(self._format, self.user_url)
104 # XXX: Changing from one fallback repository to another does not check
105 # that all the data you need is present in the new fallback.
106 # Possibly it should.
107@@ -893,7 +890,7 @@
108
109 def unbind(self):
110 """Older format branches cannot bind or unbind."""
111- raise errors.UpgradeRequired(self.base)
112+ raise errors.UpgradeRequired(self.user_url)
113
114 def last_revision(self):
115 """Return last revision id, or NULL_REVISION."""
116@@ -1059,7 +1056,7 @@
117 try:
118 return urlutils.join(self.base[:-1], parent)
119 except errors.InvalidURLJoin, e:
120- raise errors.InaccessibleParent(parent, self.base)
121+ raise errors.InaccessibleParent(parent, self.user_url)
122
123 def _get_parent_location(self):
124 raise NotImplementedError(self._get_parent_location)
125@@ -1564,7 +1561,7 @@
126 elsewhere)
127 :return: a branch in this format
128 """
129- mutter('creating branch %r in %s', self, a_bzrdir.transport.base)
130+ mutter('creating branch %r in %s', self, a_bzrdir.user_url)
131 branch_transport = a_bzrdir.get_branch_transport(self, name=name)
132 lock_map = {
133 'metadir': ('lock', lockdir.LockDir),
134@@ -1957,8 +1954,8 @@
135 if format.__class__ != self.__class__:
136 raise AssertionError("wrong format %r found for %r" %
137 (format, self))
138+ transport = a_bzrdir.get_branch_transport(None, name=name)
139 try:
140- transport = a_bzrdir.get_branch_transport(None, name=name)
141 control_files = lockable_files.LockableFiles(transport, 'lock',
142 lockdir.LockDir)
143 return self._branch_class()(_format=self,
144@@ -2162,10 +2159,10 @@
145 # this format does not implement branch itself, thus the implicit
146 # creation contract must see it as uninitializable
147 raise errors.UninitializableFormat(self)
148- mutter('creating branch reference in %s', a_bzrdir.transport.base)
149+ mutter('creating branch reference in %s', a_bzrdir.user_url)
150 branch_transport = a_bzrdir.get_branch_transport(self, name=name)
151 branch_transport.put_bytes('location',
152- target_branch.bzrdir.root_transport.base)
153+ target_branch.bzrdir.user_url)
154 branch_transport.put_bytes('format', self.get_format_string())
155 branch = self.open(
156 a_bzrdir, name, _found=True,
157@@ -2293,9 +2290,10 @@
158
159 def __str__(self):
160 if self.name is None:
161- return '%s(%r)' % (self.__class__.__name__, self.base)
162+ return '%s(%s)' % (self.__class__.__name__, self.user_url)
163 else:
164- return '%s(%r,%r)' % (self.__class__.__name__, self.base, self.name)
165+ return '%s(%s,%s)' % (self.__class__.__name__, self.user_url,
166+ self.name)
167
168 __repr__ = __str__
169
170@@ -2516,7 +2514,7 @@
171 return result
172
173 def get_stacked_on_url(self):
174- raise errors.UnstackableBranchFormat(self._format, self.base)
175+ raise errors.UnstackableBranchFormat(self._format, self.user_url)
176
177 def set_push_location(self, location):
178 """See Branch.set_push_location."""
179@@ -2712,7 +2710,7 @@
180 if _mod_revision.is_null(last_revision):
181 return
182 if last_revision not in self._lefthand_history(revision_id):
183- raise errors.AppendRevisionsOnlyViolation(self.base)
184+ raise errors.AppendRevisionsOnlyViolation(self.user_url)
185
186 def _gen_revision_history(self):
187 """Generate the revision history from last revision
188@@ -2818,7 +2816,7 @@
189 if branch_location is None:
190 return Branch.reference_parent(self, file_id, path,
191 possible_transports)
192- branch_location = urlutils.join(self.base, branch_location)
193+ branch_location = urlutils.join(self.user_url, branch_location)
194 return Branch.open(branch_location,
195 possible_transports=possible_transports)
196
197@@ -2939,7 +2937,7 @@
198 """
199
200 def get_stacked_on_url(self):
201- raise errors.UnstackableBranchFormat(self._format, self.base)
202+ raise errors.UnstackableBranchFormat(self._format, self.user_url)
203
204
205 ######################################################################
206@@ -3032,7 +3030,7 @@
207 :param verbose: Requests more detailed display of what was checked,
208 if any.
209 """
210- note('checked branch %s format %s', self.branch.base,
211+ note('checked branch %s format %s', self.branch.user_url,
212 self.branch._format)
213 for error in self.errors:
214 note('found error:%s', error)
215@@ -3367,7 +3365,7 @@
216 if local and not bound_location:
217 raise errors.LocalRequiresBoundBranch()
218 master_branch = None
219- if not local and bound_location and self.source.base != bound_location:
220+ if not local and bound_location and self.source.user_url != bound_location:
221 # not pulling from master, so we need to update master.
222 master_branch = self.target.get_master_branch(possible_transports)
223 master_branch.lock_write()
224
225=== modified file 'bzrlib/builtins.py'
226--- bzrlib/builtins.py 2010-04-23 11:11:22 +0000
227+++ bzrlib/builtins.py 2010-04-28 07:06:33 +0000
228@@ -504,9 +504,7 @@
229 if (working.has_changes()):
230 raise errors.UncommittedChanges(working)
231
232- working_path = working.bzrdir.root_transport.base
233- branch_path = working.branch.bzrdir.root_transport.base
234- if working_path != branch_path:
235+ if working.user_url != working.branch.user_url:
236 raise errors.BzrCommandError("You cannot remove the working tree"
237 " from a lightweight checkout")
238
239
240=== modified file 'bzrlib/bzrdir.py'
241--- bzrlib/bzrdir.py 2010-04-22 17:08:27 +0000
242+++ bzrlib/bzrdir.py 2010-04-28 07:06:33 +0000
243@@ -86,9 +86,42 @@
244 registry,
245 symbol_versioning,
246 )
247-
248-
249-class BzrDir(object):
250+
251+
252+class ControlComponent(object):
253+ """Abstract base class for control directory components.
254+
255+ This provides interfaces that are common across bzrdirs,
256+ repositories, branches, and workingtree control directories.
257+
258+ They all expose two urls and transports: the *user* URL is the
259+ one that stops above the control directory (eg .bzr) and that
260+ should normally be used in messages, and the *control* URL is
261+ under that in eg .bzr/checkout and is used to read the control
262+ files.
263+
264+ This can be used as a mixin and is intended to fit with
265+ foreign formats.
266+ """
267+
268+ @property
269+ def control_transport(self):
270+ raise NotImplementedError
271+
272+ @property
273+ def control_url(self):
274+ return self.control_transport.base
275+
276+ @property
277+ def user_transport(self):
278+ raise NotImplementedError
279+
280+ @property
281+ def user_url(self):
282+ return self.user_transport.base
283+
284+
285+class BzrDir(ControlComponent):
286 """A .bzr control diretory.
287
288 BzrDir instances let you create or open any of the things that can be
289@@ -261,8 +294,8 @@
290 # copied, and finally if we are copying up to a specific
291 # revision_id then we can use the pending-ancestry-result which
292 # does not require traversing all of history to describe it.
293- if (result_repo.bzrdir.root_transport.base ==
294- result.root_transport.base and not require_stacking and
295+ if (result_repo.user_url == result.user_url
296+ and not require_stacking and
297 revision_id is not None):
298 fetch_spec = graph.PendingAncestryResult(
299 [revision_id], local_repo)
300@@ -458,7 +491,7 @@
301 stop = False
302 stack_on = config.get_default_stack_on()
303 if stack_on is not None:
304- stack_on_pwd = found_bzrdir.root_transport.base
305+ stack_on_pwd = found_bzrdir.user_url
306 stop = True
307 # does it have a repository ?
308 try:
309@@ -466,8 +499,8 @@
310 except errors.NoRepositoryPresent:
311 repository = None
312 else:
313- if ((found_bzrdir.root_transport.base !=
314- self.root_transport.base) and not repository.is_shared()):
315+ if (found_bzrdir.user_url != self.user_url
316+ and not repository.is_shared()):
317 # Don't look higher, can't use a higher shared repo.
318 repository = None
319 stop = True
320@@ -669,7 +702,7 @@
321 if stop:
322 return result
323 next_transport = found_bzrdir.root_transport.clone('..')
324- if (found_bzrdir.root_transport.base == next_transport.base):
325+ if (found_bzrdir.user_url == next_transport.base):
326 # top of the file system
327 return None
328 # find the next containing bzrdir
329@@ -692,7 +725,7 @@
330 repository = found_bzrdir.open_repository()
331 except errors.NoRepositoryPresent:
332 return None, False
333- if found_bzrdir.root_transport.base == self.root_transport.base:
334+ if found_bzrdir.user_url == self.user_url:
335 return repository, True
336 elif repository.is_shared():
337 return repository, True
338@@ -814,9 +847,19 @@
339 :param _transport: the transport this dir is based at.
340 """
341 self._format = _format
342+ # these are also under the more standard names of
343+ # control_transport and user_transport
344 self.transport = _transport.clone('.bzr')
345 self.root_transport = _transport
346 self._mode_check_done = False
347+
348+ @property
349+ def user_transport(self):
350+ return self.root_transport
351+
352+ @property
353+ def control_transport(self):
354+ return self.transport
355
356 def is_control_filename(self, filename):
357 """True if filename is the name of a path which is reserved for bzrdir's.
358@@ -2692,7 +2735,7 @@
359 if isinstance(self.bzrdir.transport, local.LocalTransport):
360 self.bzrdir.get_workingtree_transport(None).delete('stat-cache')
361 self._convert_to_weaves()
362- return BzrDir.open(self.bzrdir.root_transport.base)
363+ return BzrDir.open(self.bzrdir.user_url)
364 finally:
365 self.pb.finished()
366
367@@ -2945,7 +2988,7 @@
368 try:
369 ui.ui_factory.note('starting upgrade from format 5 to 6')
370 self._convert_to_prefixed()
371- return BzrDir.open(self.bzrdir.root_transport.base)
372+ return BzrDir.open(self.bzrdir.user_url)
373 finally:
374 pb.finished()
375
376@@ -3073,7 +3116,7 @@
377 BzrDirMetaFormat1().get_format_string(),
378 mode=self.file_mode)
379 self.pb.finished()
380- return BzrDir.open(self.bzrdir.root_transport.base)
381+ return BzrDir.open(self.bzrdir.user_url)
382
383 def make_lock(self, name):
384 """Make a lock for the new control dir name."""
385@@ -3660,7 +3703,7 @@
386 try:
387 stack_on = urlutils.rebase_url(self._stack_on,
388 self._stack_on_pwd,
389- branch.bzrdir.root_transport.base)
390+ branch.user_url)
391 except errors.InvalidRebaseURLs:
392 stack_on = self._get_full_stack_on()
393 try:
394
395=== modified file 'bzrlib/check.py'
396--- bzrlib/check.py 2009-09-18 01:06:10 +0000
397+++ bzrlib/check.py 2010-04-28 07:06:33 +0000
398@@ -192,8 +192,8 @@
399
400 def _report_repo_results(self, verbose):
401 note('checked repository %s format %s',
402- self.repository.bzrdir.root_transport,
403- self.repository._format)
404+ self.repository.user_url,
405+ self.repository._format)
406 note('%6d revisions', self.checked_rev_cnt)
407 note('%6d file-ids', len(self.checked_weaves))
408 if verbose:
409@@ -451,7 +451,7 @@
410 if do_repo or do_branch or do_tree:
411 if do_repo:
412 note("Checking repository at '%s'."
413- % (repo.bzrdir.root_transport.base,))
414+ % (repo.user_url,))
415 result = repo.check(None, callback_refs=needed_refs,
416 check_repo=do_repo)
417 result.report_results(verbose)
418
419=== modified file 'bzrlib/errors.py'
420--- bzrlib/errors.py 2010-03-24 14:15:01 +0000
421+++ bzrlib/errors.py 2010-04-28 07:06:33 +0000
422@@ -2176,7 +2176,7 @@
423
424 def __init__(self, repo):
425 BzrError.__init__(self)
426- self.repo_path = repo.bzrdir.root_transport.base
427+ self.repo_path = repo.user_url
428
429
430 class InconsistentDelta(BzrError):
431@@ -2754,7 +2754,7 @@
432
433 def __init__(self, bzrdir):
434 import bzrlib.urlutils as urlutils
435- display_url = urlutils.unescape_for_display(bzrdir.root_transport.base,
436+ display_url = urlutils.unescape_for_display(bzrdir.user_url,
437 'ascii')
438 BzrError.__init__(self, bzrdir=bzrdir, display_url=display_url)
439
440@@ -2835,7 +2835,7 @@
441 more = ' ' + more
442 import bzrlib.urlutils as urlutils
443 display_url = urlutils.unescape_for_display(
444- tree.bzrdir.root_transport.base, 'ascii')
445+ tree.user_url, 'ascii')
446 BzrError.__init__(self, tree=tree, display_url=display_url, more=more)
447
448
449
450=== modified file 'bzrlib/info.py'
451--- bzrlib/info.py 2010-02-17 17:11:16 +0000
452+++ bzrlib/info.py 2010-04-28 07:06:33 +0000
453@@ -80,9 +80,9 @@
454
455 def gather_location_info(repository, branch=None, working=None):
456 locs = {}
457- repository_path = repository.bzrdir.root_transport.base
458+ repository_path = repository.user_url
459 if branch is not None:
460- branch_path = branch.bzrdir.root_transport.base
461+ branch_path = branch.user_url
462 master_path = branch.get_bound_location()
463 if master_path is None:
464 master_path = branch_path
465@@ -90,7 +90,7 @@
466 branch_path = None
467 master_path = None
468 if working:
469- working_path = working.bzrdir.root_transport.base
470+ working_path = working.user_url
471 if working_path != branch_path:
472 locs['light checkout root'] = working_path
473 if master_path != branch_path:
474@@ -420,8 +420,8 @@
475 if branch is None and tree is not None:
476 phrase = "branchless tree"
477 else:
478- if (tree is not None and tree.bzrdir.root_transport.base !=
479- branch.bzrdir.root_transport.base):
480+ if (tree is not None and tree.user_url !=
481+ branch.user_url):
482 independence = ''
483 phrase = "Lightweight checkout"
484 elif branch.get_bound_location() is not None:
485@@ -446,8 +446,7 @@
486 """
487 candidates = []
488 if (branch is not None and tree is not None and
489- branch.bzrdir.root_transport.base !=
490- tree.bzrdir.root_transport.base):
491+ branch.user_url != tree.user_url):
492 branch = None
493 repository = None
494 non_aliases = set(bzrdir.format_registry.keys())
495
496=== modified file 'bzrlib/reconcile.py'
497--- bzrlib/reconcile.py 2010-02-17 17:11:16 +0000
498+++ bzrlib/reconcile.py 2010-04-28 07:06:33 +0000
499@@ -97,7 +97,7 @@
500 def _reconcile_repository(self):
501 self.repo = self.bzrdir.find_repository()
502 ui.ui_factory.note('Reconciling repository %s' %
503- self.repo.bzrdir.root_transport.base)
504+ self.repo.user_url)
505 self.pb.update("Reconciling repository", 0, 1)
506 repo_reconciler = self.repo.reconcile(thorough=True)
507 self.inconsistent_parents = repo_reconciler.inconsistent_parents
508
509=== modified file 'bzrlib/reconfigure.py'
510--- bzrlib/reconfigure.py 2010-03-25 09:39:03 +0000
511+++ bzrlib/reconfigure.py 2010-04-28 07:06:33 +0000
512@@ -82,14 +82,13 @@
513 self.repository = None
514 self.local_repository = None
515 else:
516- if (self.repository.bzrdir.root_transport.base ==
517- self.bzrdir.root_transport.base):
518+ if (self.repository.user_url == self.bzrdir.user_url):
519 self.local_repository = self.repository
520 else:
521 self.local_repository = None
522 try:
523 branch = self.bzrdir.open_branch()
524- if branch.bzrdir.root_transport.base == bzrdir.root_transport.base:
525+ if branch.user_url == bzrdir.user_url:
526 self.local_branch = branch
527 self.referenced_branch = None
528 else:
529@@ -217,8 +216,8 @@
530 if not want_reference:
531 self._create_repository = True
532 else:
533- if want_reference and (self.repository.bzrdir.root_transport.base
534- == self.bzrdir.root_transport.base):
535+ if want_reference and (
536+ self.repository.user_url == self.bzrdir.user_url):
537 if not self.repository.is_shared():
538 self._destroy_repository = True
539 if self.referenced_branch is None:
540@@ -344,7 +343,7 @@
541 if self._create_reference:
542 reference_branch.repository.fetch(self.repository)
543 elif self.local_branch is not None and not self._destroy_branch:
544- up = self.local_branch.bzrdir.root_transport.clone('..')
545+ up = self.local_branch.user_transport.clone('..')
546 up_bzrdir = bzrdir.BzrDir.open_containing_from_transport(up)[0]
547 new_repo = up_bzrdir.find_repository()
548 new_repo.fetch(self.repository)
549
550=== modified file 'bzrlib/remote.py'
551--- bzrlib/remote.py 2010-04-20 04:16:50 +0000
552+++ bzrlib/remote.py 2010-04-28 07:06:33 +0000
553@@ -643,7 +643,8 @@
554 return self._custom_format._serializer
555
556
557-class RemoteRepository(_RpcHelper, lock._RelockDebugMixin):
558+class RemoteRepository(_RpcHelper, lock._RelockDebugMixin,
559+ bzrdir.ControlComponent):
560 """Repository accessed over rpc.
561
562 For the moment most operations are performed using local transport-backed
563@@ -692,6 +693,17 @@
564 # Additional places to query for data.
565 self._fallback_repositories = []
566
567+ @property
568+ def user_transport(self):
569+ return self.bzrdir.user_transport
570+
571+ @property
572+ def control_transport(self):
573+ # XXX: Normally you shouldn't directly get at the remote repository
574+ # transport, but I'm not sure it's worth making this method
575+ # optional -- mbp 2010-04-21
576+ return self.bzrdir.get_repository_transport(None)
577+
578 def __str__(self):
579 return "%s(%s)" % (self.__class__.__name__, self.base)
580
581@@ -1234,9 +1246,9 @@
582 # _real_branch had its get_stacked_on_url method called), then the
583 # repository to be added may already be in the _real_repositories list.
584 if self._real_repository is not None:
585- fallback_locations = [repo.bzrdir.root_transport.base for repo in
586+ fallback_locations = [repo.user_url for repo in
587 self._real_repository._fallback_repositories]
588- if repository.bzrdir.root_transport.base not in fallback_locations:
589+ if repository.user_url not in fallback_locations:
590 self._real_repository.add_fallback_repository(repository)
591
592 def _check_fallback_repository(self, repository):
593@@ -2181,7 +2193,8 @@
594 self._real_branch = None
595 # Fill out expected attributes of branch for bzrlib API users.
596 self._clear_cached_state()
597- self.base = self.bzrdir.root_transport.base
598+ # TODO: deprecate self.base in favor of user_url
599+ self.base = self.bzrdir.user_url
600 self._name = name
601 self._control_files = None
602 self._lock_mode = None
603
604=== modified file 'bzrlib/repofmt/weaverepo.py'
605--- bzrlib/repofmt/weaverepo.py 2010-04-26 13:51:08 +0000
606+++ bzrlib/repofmt/weaverepo.py 2010-04-28 07:06:33 +0000
607@@ -177,7 +177,7 @@
608 :param new_value: True to restore the default, False to disable making
609 working trees.
610 """
611- raise errors.RepositoryUpgradeRequired(self.bzrdir.root_transport.base)
612+ raise errors.RepositoryUpgradeRequired(self.user_url)
613
614 def make_working_trees(self):
615 """Returns the policy for making working trees on new branches."""
616
617=== modified file 'bzrlib/repository.py'
618--- bzrlib/repository.py 2010-04-22 17:08:27 +0000
619+++ bzrlib/repository.py 2010-04-28 07:06:33 +0000
620@@ -864,7 +864,7 @@
621 # Repositories
622
623
624-class Repository(_RelockDebugMixin):
625+class Repository(_RelockDebugMixin, bzrdir.ControlComponent):
626 """Repository holding history for one or more branches.
627
628 The repository holds and retrieves historical information including
629@@ -1291,11 +1291,10 @@
630
631 :param _format: The format of the repository on disk.
632 :param a_bzrdir: The BzrDir of the repository.
633-
634- In the future we will have a single api for all stores for
635- getting file texts, inventories and revisions, then
636- this construct will accept instances of those things.
637 """
638+ # In the future we will have a single api for all stores for
639+ # getting file texts, inventories and revisions, then
640+ # this construct will accept instances of those things.
641 super(Repository, self).__init__()
642 self._format = _format
643 # the following are part of the public API for Repository:
644@@ -1316,6 +1315,14 @@
645 # rather copying them?
646 self._safe_to_return_from_cache = False
647
648+ @property
649+ def user_transport(self):
650+ return self.bzrdir.user_transport
651+
652+ @property
653+ def control_transport(self):
654+ return self._transport
655+
656 def __repr__(self):
657 if self._fallback_repositories:
658 return '%s(%r, fallback_repositories=%r)' % (
659@@ -1469,7 +1476,7 @@
660
661 # now gather global repository information
662 # XXX: This is available for many repos regardless of listability.
663- if self.bzrdir.root_transport.listable():
664+ if self.user_transport.listable():
665 # XXX: do we want to __define len__() ?
666 # Maybe the versionedfiles object should provide a different
667 # method to get the number of keys.
668@@ -1507,7 +1514,7 @@
669
670 ret = []
671 for branches, repository in bzrdir.BzrDir.find_bzrdirs(
672- self.bzrdir.root_transport, evaluate=Evaluator()):
673+ self.user_transport, evaluate=Evaluator()):
674 if branches is not None:
675 ret.extend(branches)
676 if not using and repository is not None:
677
678=== modified file 'bzrlib/smart/bzrdir.py'
679--- bzrlib/smart/bzrdir.py 2010-02-17 17:11:16 +0000
680+++ bzrlib/smart/bzrdir.py 2010-04-28 07:06:33 +0000
681@@ -110,7 +110,7 @@
682 """Get the relative path for repository from current_transport."""
683 # the relpath of the bzrdir in the found repository gives us the
684 # path segments to pop-out.
685- relpath = repository.bzrdir.root_transport.relpath(
686+ relpath = repository.user_transport.relpath(
687 current_transport.base)
688 if len(relpath):
689 segments = ['..'] * len(relpath.split('/'))
690
691=== modified file 'bzrlib/tests/per_branch/test_branch.py'
692--- bzrlib/tests/per_branch/test_branch.py 2010-03-02 22:25:58 +0000
693+++ bzrlib/tests/per_branch/test_branch.py 2010-04-28 07:06:33 +0000
694@@ -988,3 +988,16 @@
695 merger.do_merge()
696 self.assertEqual('../branch/reference',
697 tree.branch.get_reference_info('file-id')[1])
698+
699+
700+class TestBranchControlComponent(per_branch.TestCaseWithBranch):
701+ """Branch implementations adequately implement ControlComponent."""
702+
703+ def test_urls(self):
704+ br = self.make_branch('branch')
705+ self.assertIsInstance(br.user_url, str)
706+ self.assertEqual(br.user_url, br.user_transport.base)
707+ # for all current bzrdir implementations the user dir must be
708+ # above the control dir but we might need to relax that?
709+ self.assertEqual(br.control_url.find(br.user_url), 0)
710+ self.assertEqual(br.control_url, br.control_transport.base)
711
712=== modified file 'bzrlib/tests/per_bzrdir/test_bzrdir.py'
713--- bzrlib/tests/per_bzrdir/test_bzrdir.py 2010-04-11 19:40:23 +0000
714+++ bzrlib/tests/per_bzrdir/test_bzrdir.py 2010-04-28 07:06:33 +0000
715@@ -1971,3 +1971,15 @@
716 self.assertRaises(errors.NoRepositoryPresent,
717 made_control.find_repository)
718
719+
720+class TestBzrDirControlComponent(TestCaseWithBzrDir):
721+ """BzrDir implementations adequately implement ControlComponent."""
722+
723+ def test_urls(self):
724+ bd = self.make_bzrdir('bd')
725+ self.assertIsInstance(bd.user_url, str)
726+ self.assertEqual(bd.user_url, bd.user_transport.base)
727+ # for all current bzrdir implementations the user dir must be
728+ # above the control dir but we might need to relax that?
729+ self.assertEqual(bd.control_url.find(bd.user_url), 0)
730+ self.assertEqual(bd.control_url, bd.control_transport.base)
731
732=== modified file 'bzrlib/tests/per_repository/test_repository.py'
733--- bzrlib/tests/per_repository/test_repository.py 2010-04-10 01:22:00 +0000
734+++ bzrlib/tests/per_repository/test_repository.py 2010-04-28 07:06:33 +0000
735@@ -1342,3 +1342,18 @@
736 fileobj = StringIO()
737 wt.branch.repository.create_bundle(
738 'rev1', _mod_revision.NULL_REVISION, fileobj)
739+
740+
741+
742+
743+class TestRepositoryControlComponent(per_repository.TestCaseWithRepository):
744+ """Repository implementations adequately implement ControlComponent."""
745+
746+ def test_urls(self):
747+ repo = self.make_repository('repo')
748+ self.assertIsInstance(repo.user_url, str)
749+ self.assertEqual(repo.user_url, repo.user_transport.base)
750+ # for all current bzrdir implementations the user dir must be
751+ # above the control dir but we might need to relax that?
752+ self.assertEqual(repo.control_url.find(repo.user_url), 0)
753+ self.assertEqual(repo.control_url, repo.control_transport.base)
754
755=== modified file 'bzrlib/tests/per_workingtree/test_workingtree.py'
756--- bzrlib/tests/per_workingtree/test_workingtree.py 2010-03-02 22:25:58 +0000
757+++ bzrlib/tests/per_workingtree/test_workingtree.py 2010-04-28 07:06:33 +0000
758@@ -1091,3 +1091,16 @@
759 # We should display the relative path
760 self.assertEqual('subdir/m\xb5', e.filename)
761 self.assertEqual(osutils._fs_enc, e.fs_encoding)
762+
763+
764+class TestControlComponent(TestCaseWithWorkingTree):
765+ """WorkingTree implementations adequately implement ControlComponent."""
766+
767+ def test_urls(self):
768+ wt = self.make_branch_and_tree('wt')
769+ self.assertIsInstance(wt.user_url, str)
770+ self.assertEqual(wt.user_url, wt.user_transport.base)
771+ # for all current bzrdir implementations the user dir must be
772+ # above the control dir but we might need to relax that?
773+ self.assertEqual(wt.control_url.find(wt.user_url), 0)
774+ self.assertEqual(wt.control_url, wt.control_transport.base)
775
776=== modified file 'bzrlib/tests/test_foreign.py'
777--- bzrlib/tests/test_foreign.py 2010-03-02 22:25:58 +0000
778+++ bzrlib/tests/test_foreign.py 2010-04-28 07:06:33 +0000
779@@ -90,6 +90,7 @@
780 self._format = _format
781 self._base = a_bzrdir.transport.base
782 self._ignore_fallbacks = False
783+ self.bzrdir = a_bzrdir
784 foreign.ForeignBranch.__init__(self,
785 DummyForeignVcsMapping(DummyForeignVcs()))
786 branch.BzrBranch6.__init__(self, _format, _control_files, a_bzrdir,
787
788=== modified file 'bzrlib/upgrade.py'
789--- bzrlib/upgrade.py 2010-03-25 07:34:15 +0000
790+++ bzrlib/upgrade.py 2010-04-28 07:06:33 +0000
791@@ -47,11 +47,10 @@
792 def convert(self):
793 try:
794 branch = self.bzrdir.open_branch()
795- if branch.bzrdir.root_transport.base != \
796- self.bzrdir.root_transport.base:
797+ if branch.user_url != self.bzrdir.user_url:
798 ui.ui_factory.note("This is a checkout. The branch (%s) needs to be "
799 "upgraded separately." %
800- branch.bzrdir.root_transport.base)
801+ branch.user_url)
802 del branch
803 except (errors.NotBranchError, errors.IncompatibleRepositories):
804 # might not be a format we can open without upgrading; see e.g.
805
806=== modified file 'bzrlib/workingtree.py'
807--- bzrlib/workingtree.py 2010-04-08 06:05:44 +0000
808+++ bzrlib/workingtree.py 2010-04-28 07:06:33 +0000
809@@ -29,12 +29,6 @@
810 WorkingTree.open(dir).
811 """
812
813-# TODO: Give the workingtree sole responsibility for the working inventory;
814-# remove the variable and references to it from the branch. This may require
815-# updating the commit code so as to update the inventory within the working
816-# copy, and making sure there's only one WorkingTree for any directory on disk.
817-# At the moment they may alias the inventory and have old copies of it in
818-# memory. (Now done? -- mbp 20060309)
819
820 from cStringIO import StringIO
821 import os
822@@ -174,7 +168,8 @@
823 return ''
824
825
826-class WorkingTree(bzrlib.mutabletree.MutableTree):
827+class WorkingTree(bzrlib.mutabletree.MutableTree,
828+ bzrdir.ControlComponent):
829 """Working copy tree.
830
831 The inventory is held in the `Branch` working-inventory, and the
832@@ -253,6 +248,14 @@
833 self._rules_searcher = None
834 self.views = self._make_views()
835
836+ @property
837+ def user_transport(self):
838+ return self.bzrdir.user_transport
839+
840+ @property
841+ def control_transport(self):
842+ return self._transport
843+
844 def _detect_case_handling(self):
845 wt_trans = self.bzrdir.get_workingtree_transport(None)
846 try: