Merge lp:~jelmer/bzr/colo-urls into lp:bzr

Proposed by Jelmer Vernooij
Status: Rejected
Rejected by: Jelmer Vernooij
Proposed branch: lp:~jelmer/bzr/colo-urls
Merge into: lp:bzr
Prerequisite: lp:~jelmer/bzr/use-branch-open
Diff against target: 778 lines (+247/-89)
13 files modified
bzrlib/branch.py (+45/-11)
bzrlib/builtins.py (+38/-35)
bzrlib/bzrdir.py (+29/-15)
bzrlib/errors.py (+7/-0)
bzrlib/reconcile.py (+8/-11)
bzrlib/remote.py (+8/-5)
bzrlib/switch.py (+1/-1)
bzrlib/tests/per_branch/test_branch.py (+2/-1)
bzrlib/tests/per_bzrdir_colo/test_unsupported.py (+21/-9)
bzrlib/tests/test_branch.py (+37/-0)
bzrlib/tests/test_remote.py (+1/-1)
bzrlib/tests/test_urlutils.py (+24/-0)
bzrlib/urlutils.py (+26/-0)
To merge this branch: bzr merge lp:~jelmer/bzr/colo-urls
Reviewer Review Type Date Requested Status
Robert Collins (community) Needs Fixing
Andrew Bennetts Needs Information
Review via email: mp+20860@code.launchpad.net

Description of the change

Add support for addressing colocated branches using comma's.

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) wrote :

This is ready but I'd be interested to hear how we should handle comma's in URLs. I think it's reasonable to require that colocated branch names never can contain a comma in a URL.

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

My understanding of STD 66 is that commas should bind to path segments, rather than delimit paths entirely, but I'm happy to be corrected if I'm wrong. Also, I don't see any problem with having commas or even slashes in a branch name, that's what percent-encoding is for.

So, if the plan is that ",foo" in a URL indicates a branch name of "foo", then I'd expect URLs to be parsed like:

 * http://host/path,branchname
   * branch location: http://host/path
   * branch name: branchname
 * http://host/path,branch%2Cname
   * branch location: http://host/path
   * branch name: branch,name
 * http://host/path,branchname/README.txt
   * branch location: http://host/path
   * branch name: branchname
   * path inside branch: README.txt [e.g. I want to be able to use this with bzr log, bzr cat, and bzr annotate]

I wasn't at Strasbourg. Did we decide against a key/value syntax (like ",branch=name"), or is it still intended that we might support e.g. ",revid=AAAA..." at some point?

(What happened to the promised update to doc/developers/colocated-branches.txt?)

review: Needs Information
Revision history for this message
Martin Pool (mbp) wrote :

I'm still a bit concerned that this is passing the branch name around so many different places, rather than currying that into an object. I guess the problem here is that the places you're updating here are "places a branch could be" not necessarily an existing open branch. Aside from that it looks pretty good, and perhaps it's easier to bring it in and then change it later.

- this_branch._format.set_reference(this_branch.bzrdir, other_branch)
+ this_branch._format.set_reference(this_branch.bzrdir, None,
+ other_branch)

Inserting parameters into the middle is a somewhat noxious way to change the api in Python. It's ok to have an api bump but perhaps we should add to the end?

Revision history for this message
Robert Collins (lifeless) wrote :

actually this is clearly bogus: other_branch *is a branch object*, there is
no need for the branch name to be there - its known already by the branch
object.

I need to go through this with a fine comb I think, this makes me worry
other changes that aren't needed are being done.

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

@Robert: Did you unpack your comb ? :-)

Revision history for this message
Jelmer Vernooij (jelmer) wrote :

Rob, we're setting a branch reference in a local bzrdir to another branch. The name of the local colocated branch is specified by the new parameter.

Revision history for this message
Robert Collins (lifeless) wrote :

Am looking at this, iz not forgotten.

Revision history for this message
Robert Collins (lifeless) wrote :

Oh, and @spiv - yes, , should be per segment in the path, not whole-string - thats the point of this ;)

Revision history for this message
Robert Collins (lifeless) wrote :

Ok, some notes:
 - if we have to edit the url before passing to transport, transport isn't supporting segment parameters properly - need some tests on that layer

 e.g. opening foo and foo,name=bar should get two equal transports. (except for parameters we decide transports should support - e.g. passive ftp might be one (though we have a syntax for that already)

So - further discussion:
 - Transport is our 'access a url' abstraction; it should take care of getting parameters parsed, exposing the parsed parameters to BzrDir.open_from_transport, and so on.
 - BzrDir should get the default branch from the transport parameters.
 - other code shouldn't need to change for this part of the conversion
 - we should end up being able to open '/foo/bar,name=gam' as a result ! (without changing all the higher layers).

review: Needs Fixing

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'bzrlib/branch.py'
--- bzrlib/branch.py 2010-04-10 18:03:54 +0000
+++ bzrlib/branch.py 2010-04-17 17:41:17 +0000
@@ -63,6 +63,34 @@
63BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"63BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n"
6464
6565
66def split_colocated_name(url):
67 """Extract the colocated branch name from a URL.
68
69 :param url: The original URL
70 :return: Tuple with new URL and colocated branch name (None if not specified)
71 """
72 if url is None:
73 return (None, None)
74 (base, subsegments) = urlutils.split_subsegments(url)
75 if len(subsegments) == 0:
76 return (url, None)
77 if len(subsegments) > 1:
78 raise errors.InvalidURL(url)
79 return (base, subsegments[0])
80
81
82def join_colocated_name(base, branch_name):
83 """Combine a location and a colocated branch name URL.
84
85 :param base: Base location
86 :param branch_name: Co-located branch name
87 :return: A URL that addresses a colocated branch
88 """
89 if branch_name is None:
90 return base
91 return urlutils.join_subsegments(base, branch_name)
92
93
66# TODO: Maybe include checks for common corruption of newlines, etc?94# TODO: Maybe include checks for common corruption of newlines, etc?
6795
68# TODO: Some operations like log might retrieve the same revisions96# TODO: Some operations like log might retrieve the same revisions
@@ -159,15 +187,16 @@
159 return [('revision-existence', revid), ('lefthand-distance', revid)]187 return [('revision-existence', revid), ('lefthand-distance', revid)]
160188
161 @staticmethod189 @staticmethod
162 def open(base, _unsupported=False, possible_transports=None):190 def open(url, _unsupported=False, possible_transports=None):
163 """Open the branch rooted at base.191 """Open the branch rooted at base.
164192
165 For instance, if the branch is at URL/.bzr/branch,193 For instance, if the branch is at URL/.bzr/branch,
166 Branch.open(URL) -> a Branch instance.194 Branch.open(URL) -> a Branch instance.
167 """195 """
196 (base, branch_name) = split_colocated_name(url)
168 control = bzrdir.BzrDir.open(base, _unsupported,197 control = bzrdir.BzrDir.open(base, _unsupported,
169 possible_transports=possible_transports)198 possible_transports=possible_transports)
170 return control.open_branch(unsupported=_unsupported)199 return control.open_branch(name=branch_name, unsupported=_unsupported)
171200
172 @staticmethod201 @staticmethod
173 def open_from_transport(transport, name=None, _unsupported=False):202 def open_from_transport(transport, name=None, _unsupported=False):
@@ -187,9 +216,10 @@
187 format, UnknownFormatError or UnsupportedFormatError are raised.216 format, UnknownFormatError or UnsupportedFormatError are raised.
188 If there is one, it is returned, along with the unused portion of url.217 If there is one, it is returned, along with the unused portion of url.
189 """218 """
190 control, relpath = bzrdir.BzrDir.open_containing(url,219 (base, branch_name) = split_colocated_name(url)
220 control, relpath = bzrdir.BzrDir.open_containing(base,
191 possible_transports)221 possible_transports)
192 return control.open_branch(), relpath222 return control.open_branch(name=branch_name), relpath
193223
194 def _push_should_merge_tags(self):224 def _push_should_merge_tags(self):
195 """Should _basic_push merge this branch's tags into the target?225 """Should _basic_push merge this branch's tags into the target?
@@ -1335,6 +1365,8 @@
1335 """1365 """
1336 # XXX: Fix the bzrdir API to allow getting the branch back from the1366 # XXX: Fix the bzrdir API to allow getting the branch back from the
1337 # clone call. Or something. 20090224 RBC/spiv.1367 # clone call. Or something. 20090224 RBC/spiv.
1368 # XXX: Should this perhaps clone colocated branches as well,
1369 # rather than just the default branch? 20100319 JRV
1338 if revision_id is None:1370 if revision_id is None:
1339 revision_id = self.last_revision()1371 revision_id = self.last_revision()
1340 dir_to = self.bzrdir.clone_on_transport(to_transport,1372 dir_to = self.bzrdir.clone_on_transport(to_transport,
@@ -1510,7 +1542,7 @@
1510 """Return the current default format."""1542 """Return the current default format."""
1511 return klass._default_format1543 return klass._default_format
15121544
1513 def get_reference(self, a_bzrdir):1545 def get_reference(self, a_bzrdir, branch_name=None):
1514 """Get the target reference of the branch in a_bzrdir.1546 """Get the target reference of the branch in a_bzrdir.
15151547
1516 format probing must have been completed before calling1548 format probing must have been completed before calling
@@ -1518,12 +1550,13 @@
1518 in a_bzrdir is correct.1550 in a_bzrdir is correct.
15191551
1520 :param a_bzrdir: The bzrdir to get the branch data from.1552 :param a_bzrdir: The bzrdir to get the branch data from.
1553 :param branch_name: Name of the colocated branch to fetch
1521 :return: None if the branch is not a reference branch.1554 :return: None if the branch is not a reference branch.
1522 """1555 """
1523 return None1556 return None
15241557
1525 @classmethod1558 @classmethod
1526 def set_reference(self, a_bzrdir, to_branch):1559 def set_reference(self, a_bzrdir, branch_name, to_branch):
1527 """Set the target reference of the branch in a_bzrdir.1560 """Set the target reference of the branch in a_bzrdir.
15281561
1529 format probing must have been completed before calling1562 format probing must have been completed before calling
@@ -1531,6 +1564,7 @@
1531 in a_bzrdir is correct.1564 in a_bzrdir is correct.
15321565
1533 :param a_bzrdir: The bzrdir to set the branch reference for.1566 :param a_bzrdir: The bzrdir to set the branch reference for.
1567 :param branch_name: Name of colocated branch to set, None for default
1534 :param to_branch: branch that the checkout is to reference1568 :param to_branch: branch that the checkout is to reference
1535 """1569 """
1536 raise NotImplementedError(self.set_reference)1570 raise NotImplementedError(self.set_reference)
@@ -2048,14 +2082,14 @@
2048 """See BranchFormat.get_format_description()."""2082 """See BranchFormat.get_format_description()."""
2049 return "Checkout reference format 1"2083 return "Checkout reference format 1"
20502084
2051 def get_reference(self, a_bzrdir):2085 def get_reference(self, a_bzrdir, branch_name=None):
2052 """See BranchFormat.get_reference()."""2086 """See BranchFormat.get_reference()."""
2053 transport = a_bzrdir.get_branch_transport(None)2087 transport = a_bzrdir.get_branch_transport(branch_name)
2054 return transport.get_bytes('location')2088 return transport.get_bytes('location')
20552089
2056 def set_reference(self, a_bzrdir, to_branch):2090 def set_reference(self, a_bzrdir, branch_name, to_branch):
2057 """See BranchFormat.set_reference()."""2091 """See BranchFormat.set_reference()."""
2058 transport = a_bzrdir.get_branch_transport(None)2092 transport = a_bzrdir.get_branch_transport(branch_name)
2059 location = transport.put_bytes('location', to_branch.base)2093 location = transport.put_bytes('location', to_branch.base)
20602094
2061 def initialize(self, a_bzrdir, name=None, target_branch=None):2095 def initialize(self, a_bzrdir, name=None, target_branch=None):
@@ -2110,7 +2144,7 @@
2110 raise AssertionError("wrong format %r found for %r" %2144 raise AssertionError("wrong format %r found for %r" %
2111 (format, self))2145 (format, self))
2112 if location is None:2146 if location is None:
2113 location = self.get_reference(a_bzrdir)2147 location = self.get_reference(a_bzrdir, name)
2114 real_bzrdir = bzrdir.BzrDir.open(2148 real_bzrdir = bzrdir.BzrDir.open(
2115 location, possible_transports=possible_transports)2149 location, possible_transports=possible_transports)
2116 result = real_bzrdir.open_branch(name=name, 2150 result = real_bzrdir.open_branch(name=name,
21172151
=== modified file 'bzrlib/builtins.py'
--- bzrlib/builtins.py 2010-04-15 15:03:15 +0000
+++ bzrlib/builtins.py 2010-04-17 17:41:17 +0000
@@ -52,7 +52,11 @@
52 urlutils,52 urlutils,
53 views,53 views,
54 )54 )
55from bzrlib.branch import Branch55from bzrlib.branch import (
56 Branch,
57 join_colocated_name,
58 split_colocated_name,
59 )
56from bzrlib.conflicts import ConflictList60from bzrlib.conflicts import ConflictList
57from bzrlib.transport import memory61from bzrlib.transport import memory
58from bzrlib.revisionspec import RevisionSpec, RevisionInfo62from bzrlib.revisionspec import RevisionSpec, RevisionInfo
@@ -1230,23 +1234,24 @@
1230 revision_id = br_from.last_revision()1234 revision_id = br_from.last_revision()
1231 if to_location is None:1235 if to_location is None:
1232 to_location = urlutils.derive_to_location(from_location)1236 to_location = urlutils.derive_to_location(from_location)
1233 to_transport = transport.get_transport(to_location)1237 to_base, to_branch_name = split_colocated_name(to_location)
1238 to_transport = transport.get_transport(to_base)
1234 try:1239 try:
1235 to_transport.mkdir('.')1240 to_transport.mkdir('.')
1236 except errors.FileExists:1241 except errors.FileExists:
1237 if not use_existing_dir:1242 if not use_existing_dir:
1238 raise errors.BzrCommandError('Target directory "%s" '1243 raise errors.BzrCommandError('Target directory "%s" '
1239 'already exists.' % to_location)1244 'already exists.' % to_base)
1240 else:1245 else:
1241 try:1246 try:
1242 bzrdir.BzrDir.open_from_transport(to_transport)1247 bzrdir.BzrDir.open_from_transport(to_transport)
1243 except errors.NotBranchError:1248 except errors.NotBranchError:
1244 pass1249 pass
1245 else:1250 else:
1246 raise errors.AlreadyBranchError(to_location)1251 raise errors.AlreadyBranchError(to_base)
1247 except errors.NoSuchFile:1252 except errors.NoSuchFile:
1248 raise errors.BzrCommandError('Parent of "%s" does not exist.'1253 raise errors.BzrCommandError('Parent of "%s" does not exist.'
1249 % to_location)1254 % to_base)
1250 try:1255 try:
1251 # preserve whatever source format we have.1256 # preserve whatever source format we have.
1252 dir = br_from.bzrdir.sprout(to_transport.base, revision_id,1257 dir = br_from.bzrdir.sprout(to_transport.base, revision_id,
@@ -1256,7 +1261,7 @@
1256 force_new_repo=standalone,1261 force_new_repo=standalone,
1257 create_tree_if_local=not no_tree,1262 create_tree_if_local=not no_tree,
1258 source_branch=br_from)1263 source_branch=br_from)
1259 branch = dir.open_branch()1264 branch = dir.open_branch(to_branch_name)
1260 except errors.NoSuchRevision:1265 except errors.NoSuchRevision:
1261 to_transport.delete_tree('.')1266 to_transport.delete_tree('.')
1262 msg = "The branch %s has no revision %s." % (from_location,1267 msg = "The branch %s has no revision %s." % (from_location,
@@ -1731,7 +1736,8 @@
1731 if location is None:1736 if location is None:
1732 location = u'.'1737 location = u'.'
17331738
1734 to_transport = transport.get_transport(location)1739 (base, branch_name) = split_colocated_name(location)
1740 to_transport = transport.get_transport(base)
17351741
1736 # The path has to exist to initialize a1742 # The path has to exist to initialize a
1737 # branch inside of it.1743 # branch inside of it.
@@ -1746,7 +1752,7 @@
1746 " does not exist."1752 " does not exist."
1747 "\nYou may supply --create-prefix to create all"1753 "\nYou may supply --create-prefix to create all"
1748 " leading parent directories."1754 " leading parent directories."
1749 % location)1755 % base)
1750 to_transport.create_prefix()1756 to_transport.create_prefix()
17511757
1752 try:1758 try:
@@ -1754,17 +1760,18 @@
1754 except errors.NotBranchError:1760 except errors.NotBranchError:
1755 # really a NotBzrDir error...1761 # really a NotBzrDir error...
1756 create_branch = bzrdir.BzrDir.create_branch_convenience1762 create_branch = bzrdir.BzrDir.create_branch_convenience
1757 branch = create_branch(to_transport.base, format=format,1763 branch = create_branch(
1758 possible_transports=[to_transport])1764 join_colocated_name(to_transport.base, branch_name),
1765 format=format, possible_transports=[to_transport])
1759 a_bzrdir = branch.bzrdir1766 a_bzrdir = branch.bzrdir
1760 else:1767 else:
1761 from bzrlib.transport.local import LocalTransport1768 from bzrlib.transport.local import LocalTransport
1762 if a_bzrdir.has_branch():1769 if a_bzrdir.has_branch(branch_name):
1763 if (isinstance(to_transport, LocalTransport)1770 if (isinstance(to_transport, LocalTransport)
1764 and not a_bzrdir.has_workingtree()):1771 and not a_bzrdir.has_workingtree()):
1765 raise errors.BranchExistsWithoutWorkingTree(location)1772 raise errors.BranchExistsWithoutWorkingTree(location)
1766 raise errors.AlreadyBranchError(location)1773 raise errors.AlreadyBranchError(location)
1767 branch = a_bzrdir.create_branch()1774 branch = a_bzrdir.create_branch(branch_name)
1768 a_bzrdir.create_workingtree()1775 a_bzrdir.create_workingtree()
1769 if append_revisions_only:1776 if append_revisions_only:
1770 try:1777 try:
@@ -2365,8 +2372,7 @@
2365 location = revision[0].get_branch()2372 location = revision[0].get_branch()
2366 else:2373 else:
2367 location = '.'2374 location = '.'
2368 dir, relpath = bzrdir.BzrDir.open_containing(location)2375 b, relpath = Branch.open_containing(location)
2369 b = dir.open_branch()
2370 b.lock_read()2376 b.lock_read()
2371 self.add_cleanup(b.unlock)2377 self.add_cleanup(b.unlock)
2372 rev1, rev2 = _get_revision_range(revision, b, self.name())2378 rev1, rev2 = _get_revision_range(revision, b, self.name())
@@ -4732,13 +4738,8 @@
4732 revision=None, force=False, local=False):4738 revision=None, force=False, local=False):
4733 if location is None:4739 if location is None:
4734 location = u'.'4740 location = u'.'
4735 control, relpath = bzrdir.BzrDir.open_containing(location)4741 tree, b, relpath = bzrdir.BzrDir.open_containing_tree_or_branch(
4736 try:4742 location)
4737 tree = control.open_workingtree()
4738 b = tree.branch
4739 except (errors.NoWorkingTree, errors.NotLocalUrl):
4740 tree = None
4741 b = control.open_branch()
47424743
4743 if tree is not None:4744 if tree is not None:
4744 tree.lock_write()4745 tree.lock_write()
@@ -5543,51 +5544,53 @@
5543 from bzrlib import switch5544 from bzrlib import switch
5544 tree_location = '.'5545 tree_location = '.'
5545 revision = _get_one_revision('switch', revision)5546 revision = _get_one_revision('switch', revision)
5546 control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]5547 tree_base, branch_name = split_colocated_name(tree_location)
5548 control_dir = bzrdir.BzrDir.open_containing(tree_base)[0]
5547 if to_location is None:5549 if to_location is None:
5548 if revision is None:5550 if revision is None:
5549 raise errors.BzrCommandError('You must supply either a'5551 raise errors.BzrCommandError('You must supply either a'
5550 ' revision or a location')5552 ' revision or a location')
5551 to_location = '.'5553 to_location = '.'
5552 try:5554 try:
5553 branch = control_dir.open_branch()5555 branch = control_dir.open_branch(branch_name)
5554 had_explicit_nick = branch.get_config().has_explicit_nickname()5556 had_explicit_nick = branch.get_config().has_explicit_nickname()
5555 except errors.NotBranchError:5557 except errors.NotBranchError:
5556 branch = None5558 branch = None
5557 had_explicit_nick = False5559 had_explicit_nick = False
5560 to_base, to_branch_name = split_colocated_name(to_location)
5558 if create_branch:5561 if create_branch:
5559 if branch is None:5562 if branch is None:
5560 raise errors.BzrCommandError('cannot create branch without'5563 raise errors.BzrCommandError('cannot create branch without'
5561 ' source branch')5564 ' source branch')
5562 to_location = directory_service.directories.dereference(5565 to_base = directory_service.directories.dereference(
5563 to_location)5566 to_base)
5564 if '/' not in to_location and '\\' not in to_location:5567 if '/' not in to_base and '\\' not in to_base:
5565 # This path is meant to be relative to the existing branch5568 # This path is meant to be relative to the existing branch
5566 this_url = self._get_branch_location(control_dir)5569 this_url = self._get_branch_location(control_dir)
5567 to_location = urlutils.join(this_url, '..', to_location)5570 to_base = urlutils.join(this_url, '..', to_base)
5568 to_branch = branch.bzrdir.sprout(to_location,5571 to_branch = branch.bzrdir.sprout(to_base,
5569 possible_transports=[branch.bzrdir.root_transport],5572 possible_transports=[branch.bzrdir.root_transport],
5570 source_branch=branch).open_branch()5573 source_branch=branch).open_branch(to_branch_name)
5571 else:5574 else:
5572 try:5575 try:
5573 to_branch = Branch.open(to_location)5576 to_branch = Branch.open(to_location)
5574 except errors.NotBranchError:5577 except errors.NotBranchError:
5575 this_url = self._get_branch_location(control_dir)5578 this_url = self._get_branch_location(control_dir, branch_name)
5576 to_branch = Branch.open(5579 to_branch = Branch.open(
5577 urlutils.join(this_url, '..', to_location))5580 urlutils.join(this_url, '..', to_location))
5578 if revision is not None:5581 if revision is not None:
5579 revision = revision.as_revision_id(to_branch)5582 revision = revision.as_revision_id(to_branch)
5580 switch.switch(control_dir, to_branch, force, revision_id=revision)5583 switch.switch(control_dir, to_branch, force, revision_id=revision)
5581 if had_explicit_nick:5584 if had_explicit_nick:
5582 branch = control_dir.open_branch() #get the new branch!5585 branch = control_dir.open_branch(to_branch_name) #get the new branch!
5583 branch.nick = to_branch.nick5586 branch.nick = to_branch.nick
5584 note('Switched to branch: %s',5587 note('Switched to branch: %s',
5585 urlutils.unescape_for_display(to_branch.base, 'utf-8'))5588 urlutils.unescape_for_display(to_branch.base, 'utf-8'))
55865589
5587 def _get_branch_location(self, control_dir):5590 def _get_branch_location(self, control_dir, branch_name):
5588 """Return location of branch for this control dir."""5591 """Return location of branch for this control dir."""
5589 try:5592 try:
5590 this_branch = control_dir.open_branch()5593 this_branch = control_dir.open_branch(branch_name)
5591 # This may be a heavy checkout, where we want the master branch5594 # This may be a heavy checkout, where we want the master branch
5592 master_location = this_branch.get_bound_location()5595 master_location = this_branch.get_bound_location()
5593 if master_location is not None:5596 if master_location is not None:
@@ -5597,7 +5600,7 @@
5597 except errors.NotBranchError:5600 except errors.NotBranchError:
5598 format = control_dir.find_branch_format()5601 format = control_dir.find_branch_format()
5599 if getattr(format, 'get_reference', None) is not None:5602 if getattr(format, 'get_reference', None) is not None:
5600 return format.get_reference(control_dir)5603 return format.get_reference(control_dir, branch_name)
5601 else:5604 else:
5602 return control_dir.root_transport.base5605 return control_dir.root_transport.base
56035606
56045607
=== modified file 'bzrlib/bzrdir.py'
--- bzrlib/bzrdir.py 2010-04-15 15:03:15 +0000
+++ bzrlib/bzrdir.py 2010-04-17 17:41:17 +0000
@@ -220,7 +220,7 @@
220 except errors.NoRepositoryPresent:220 except errors.NoRepositoryPresent:
221 local_repo = None221 local_repo = None
222 try:222 try:
223 local_branch = self.open_branch()223 local_branch = self.open_branch() # XXX Deal with colocated branches
224 except errors.NotBranchError:224 except errors.NotBranchError:
225 local_branch = None225 local_branch = None
226 else:226 else:
@@ -704,13 +704,18 @@
704 raise errors.NoRepositoryPresent(self)704 raise errors.NoRepositoryPresent(self)
705 return found_repo705 return found_repo
706706
707 def get_branch_reference(self):707 def get_branch_reference(self, name=None):
708 """Return the referenced URL for the branch in this bzrdir.708 """Return the referenced URL for the branch in this bzrdir.
709709
710 :param name: Optional colocated branch name
710 :raises NotBranchError: If there is no Branch.711 :raises NotBranchError: If there is no Branch.
712 :raises NoColocatedBranchSupport: If a branch name was specified
713 but colocated branches are not supported.
711 :return: The URL the branch in this bzrdir references if it is a714 :return: The URL the branch in this bzrdir references if it is a
712 reference branch, or None for regular branches.715 reference branch, or None for regular branches.
713 """716 """
717 if name is not None:
718 raise errors.NoColocatedBranchSupport(self)
714 return None719 return None
715720
716 def get_branch_transport(self, branch_format, name=None):721 def get_branch_transport(self, branch_format, name=None):
@@ -951,9 +956,11 @@
951 raise errors.NotBranchError(path=url)956 raise errors.NotBranchError(path=url)
952 a_transport = new_t957 a_transport = new_t
953958
954 def _get_tree_branch(self):959 def _get_tree_branch(self, name=None):
955 """Return the branch and tree, if any, for this bzrdir.960 """Return the branch and tree, if any, for this bzrdir.
956961
962 :param name: Name of colocated branch to open.
963
957 Return None for tree if not present or inaccessible.964 Return None for tree if not present or inaccessible.
958 Raise NotBranchError if no branch is present.965 Raise NotBranchError if no branch is present.
959 :return: (tree, branch)966 :return: (tree, branch)
@@ -962,9 +969,12 @@
962 tree = self.open_workingtree()969 tree = self.open_workingtree()
963 except (errors.NoWorkingTree, errors.NotLocalUrl):970 except (errors.NoWorkingTree, errors.NotLocalUrl):
964 tree = None971 tree = None
965 branch = self.open_branch()972 branch = self.open_branch(name=name)
966 else:973 else:
967 branch = tree.branch974 if name is not None:
975 branch = self.open_branch(name=name)
976 else:
977 branch = tree.branch
968 return tree, branch978 return tree, branch
969979
970 @classmethod980 @classmethod
@@ -976,8 +986,10 @@
976 raised986 raised
977 :return: (tree, branch)987 :return: (tree, branch)
978 """988 """
989 from bzrlib.branch import split_colocated_name
990 (location, branch_name) = split_colocated_name(location)
979 bzrdir = klass.open(location)991 bzrdir = klass.open(location)
980 return bzrdir._get_tree_branch()992 return bzrdir._get_tree_branch(branch_name)
981993
982 @classmethod994 @classmethod
983 def open_containing_tree_or_branch(klass, location):995 def open_containing_tree_or_branch(klass, location):
@@ -989,8 +1001,10 @@
989 raised1001 raised
990 relpath is the portion of the path that is contained by the branch.1002 relpath is the portion of the path that is contained by the branch.
991 """1003 """
1004 from bzrlib.branch import split_colocated_name
1005 (location, branch_name) = split_colocated_name(location)
992 bzrdir, relpath = klass.open_containing(location)1006 bzrdir, relpath = klass.open_containing(location)
993 tree, branch = bzrdir._get_tree_branch()1007 tree, branch = bzrdir._get_tree_branch(branch_name)
994 return tree, branch, relpath1008 return tree, branch, relpath
9951009
996 @classmethod1010 @classmethod
@@ -1261,13 +1275,13 @@
1261 return result1275 return result
12621276
1263 def push_branch(self, source, revision_id=None, overwrite=False, 1277 def push_branch(self, source, revision_id=None, overwrite=False,
1264 remember=False, create_prefix=False):1278 remember=False, create_prefix=False, branch_name=None):
1265 """Push the source branch into this BzrDir."""1279 """Push the source branch into this BzrDir."""
1266 br_to = None1280 br_to = None
1267 # If we can open a branch, use its direct repository, otherwise see1281 # If we can open a branch, use its direct repository, otherwise see
1268 # if there is a repository without a branch.1282 # if there is a repository without a branch.
1269 try:1283 try:
1270 br_to = self.open_branch()1284 br_to = self.open_branch(branch_name)
1271 except errors.NotBranchError:1285 except errors.NotBranchError:
1272 # Didn't find a branch, can we find a repository?1286 # Didn't find a branch, can we find a repository?
1273 repository_to = self.find_repository()1287 repository_to = self.find_repository()
@@ -1652,13 +1666,13 @@
1652 def destroy_workingtree_metadata(self):1666 def destroy_workingtree_metadata(self):
1653 self.transport.delete_tree('checkout')1667 self.transport.delete_tree('checkout')
16541668
1655 def find_branch_format(self):1669 def find_branch_format(self, name=None):
1656 """Find the branch 'format' for this bzrdir.1670 """Find the branch 'format' for this bzrdir.
16571671
1658 This might be a synthetic object for e.g. RemoteBranch and SVN.1672 This might be a synthetic object for e.g. RemoteBranch and SVN.
1659 """1673 """
1660 from bzrlib.branch import BranchFormat1674 from bzrlib.branch import BranchFormat
1661 return BranchFormat.find_format(self)1675 return BranchFormat.find_format(self, name=name)
16621676
1663 def _get_mkdir_mode(self):1677 def _get_mkdir_mode(self):
1664 """Figure out the mode to use when creating a bzrdir subdir."""1678 """Figure out the mode to use when creating a bzrdir subdir."""
@@ -1666,11 +1680,11 @@
1666 lockable_files.TransportLock)1680 lockable_files.TransportLock)
1667 return temp_control._dir_mode1681 return temp_control._dir_mode
16681682
1669 def get_branch_reference(self):1683 def get_branch_reference(self, name=None):
1670 """See BzrDir.get_branch_reference()."""1684 """See BzrDir.get_branch_reference()."""
1671 from bzrlib.branch import BranchFormat1685 from bzrlib.branch import BranchFormat
1672 format = BranchFormat.find_format(self)1686 format = BranchFormat.find_format(self, name=name)
1673 return format.get_reference(self)1687 return format.get_reference(self, name=name)
16741688
1675 def get_branch_transport(self, branch_format, name=None):1689 def get_branch_transport(self, branch_format, name=None):
1676 """See BzrDir.get_branch_transport()."""1690 """See BzrDir.get_branch_transport()."""
@@ -1770,7 +1784,7 @@
1770 def open_branch(self, name=None, unsupported=False,1784 def open_branch(self, name=None, unsupported=False,
1771 ignore_fallbacks=False):1785 ignore_fallbacks=False):
1772 """See BzrDir.open_branch."""1786 """See BzrDir.open_branch."""
1773 format = self.find_branch_format()1787 format = self.find_branch_format(name=name)
1774 self._check_supported(format, unsupported)1788 self._check_supported(format, unsupported)
1775 return format.open(self, name=name,1789 return format.open(self, name=name,
1776 _found=True, ignore_fallbacks=ignore_fallbacks)1790 _found=True, ignore_fallbacks=ignore_fallbacks)
17771791
=== modified file 'bzrlib/errors.py'
--- bzrlib/errors.py 2010-03-24 14:15:01 +0000
+++ bzrlib/errors.py 2010-04-17 17:41:17 +0000
@@ -3134,3 +3134,10 @@
3134 def __init__(self, bzrdir):3134 def __init__(self, bzrdir):
3135 self.bzrdir = bzrdir3135 self.bzrdir = bzrdir
31363136
3137
3138class InvalidColocatedBranchName(BzrError):
3139
3140 _fmt = "%(branch_name)s is an invalid name for a colocated branch."
3141
3142 def __init__(self, branch_name):
3143 self.branch_name = branch_name
31373144
=== modified file 'bzrlib/reconcile.py'
--- bzrlib/reconcile.py 2010-02-17 17:11:16 +0000
+++ bzrlib/reconcile.py 2010-04-17 17:41:17 +0000
@@ -80,19 +80,16 @@
8080
81 def _reconcile(self):81 def _reconcile(self):
82 """Helper function for performing reconciliation."""82 """Helper function for performing reconciliation."""
83 self._reconcile_branch()83 self._reconcile_branches()
84 self._reconcile_repository()84 self._reconcile_repository()
8585
86 def _reconcile_branch(self):86 def _reconcile_branches(self):
87 try:87 self.fixed_branch_history = True
88 self.branch = self.bzrdir.open_branch()88 for branch in self.bzrdir.list_branches():
89 except errors.NotBranchError:89 ui.ui_factory.note('Reconciling branch %s' % branch.base)
90 # Nothing to check here90 branch_reconciler = branch.reconcile(thorough=True)
91 self.fixed_branch_history = None91 self.fixed_branch_history = (self.fixed_branch_history and
92 return92 branch_reconciler.fixed_history)
93 ui.ui_factory.note('Reconciling branch %s' % self.branch.base)
94 branch_reconciler = self.branch.reconcile(thorough=True)
95 self.fixed_branch_history = branch_reconciler.fixed_history
9693
97 def _reconcile_repository(self):94 def _reconcile_repository(self):
98 self.repo = self.bzrdir.find_repository()95 self.repo = self.bzrdir.find_repository()
9996
=== modified file 'bzrlib/remote.py'
--- bzrlib/remote.py 2010-03-21 21:39:33 +0000
+++ bzrlib/remote.py 2010-04-17 17:41:17 +0000
@@ -271,16 +271,19 @@
271 def create_workingtree(self, revision_id=None, from_branch=None):271 def create_workingtree(self, revision_id=None, from_branch=None):
272 raise errors.NotLocalUrl(self.transport.base)272 raise errors.NotLocalUrl(self.transport.base)
273273
274 def find_branch_format(self):274 def find_branch_format(self, name=None):
275 """Find the branch 'format' for this bzrdir.275 """Find the branch 'format' for this bzrdir.
276276
277 This might be a synthetic object for e.g. RemoteBranch and SVN.277 This might be a synthetic object for e.g. RemoteBranch and SVN.
278 """278 """
279 b = self.open_branch()279 b = self.open_branch(name=name)
280 return b._format280 return b._format
281281
282 def get_branch_reference(self):282 def get_branch_reference(self, name=None):
283 """See BzrDir.get_branch_reference()."""283 """See BzrDir.get_branch_reference()."""
284 if name is not None:
285 # XXX JRV20100304: Support opening colocated branches
286 raise errors.NoColocatedBranchSupport(self)
284 response = self._get_branch_reference()287 response = self._get_branch_reference()
285 if response[0] == 'ref':288 if response[0] == 'ref':
286 return response[1]289 return response[1]
@@ -317,9 +320,9 @@
317 raise errors.UnexpectedSmartServerResponse(response)320 raise errors.UnexpectedSmartServerResponse(response)
318 return response321 return response
319322
320 def _get_tree_branch(self):323 def _get_tree_branch(self, name=None):
321 """See BzrDir._get_tree_branch()."""324 """See BzrDir._get_tree_branch()."""
322 return None, self.open_branch()325 return None, self.open_branch(name=name)
323326
324 def open_branch(self, name=None, unsupported=False,327 def open_branch(self, name=None, unsupported=False,
325 ignore_fallbacks=False):328 ignore_fallbacks=False):
326329
=== modified file 'bzrlib/switch.py'
--- bzrlib/switch.py 2010-02-17 17:11:16 +0000
+++ bzrlib/switch.py 2010-04-17 17:41:17 +0000
@@ -70,7 +70,7 @@
70 branch_format = control.find_branch_format()70 branch_format = control.find_branch_format()
71 if branch_format.get_reference(control) is not None:71 if branch_format.get_reference(control) is not None:
72 # Lightweight checkout: update the branch reference72 # Lightweight checkout: update the branch reference
73 branch_format.set_reference(control, to_branch)73 branch_format.set_reference(control, None, to_branch)
74 else:74 else:
75 b = control.open_branch()75 b = control.open_branch()
76 bound_branch = b.get_bound_location()76 bound_branch = b.get_bound_location()
7777
=== modified file 'bzrlib/tests/per_branch/test_branch.py'
--- bzrlib/tests/per_branch/test_branch.py 2010-03-02 22:25:58 +0000
+++ bzrlib/tests/per_branch/test_branch.py 2010-04-17 17:41:17 +0000
@@ -668,7 +668,8 @@
668 this_branch = self.make_branch('this')668 this_branch = self.make_branch('this')
669 other_branch = self.make_branch('other')669 other_branch = self.make_branch('other')
670 try:670 try:
671 this_branch._format.set_reference(this_branch.bzrdir, other_branch)671 this_branch._format.set_reference(this_branch.bzrdir, None,
672 other_branch)
672 except NotImplementedError:673 except NotImplementedError:
673 # that's ok674 # that's ok
674 pass675 pass
675676
=== modified file 'bzrlib/tests/per_bzrdir_colo/test_unsupported.py'
--- bzrlib/tests/per_bzrdir_colo/test_unsupported.py 2010-04-11 19:40:23 +0000
+++ bzrlib/tests/per_bzrdir_colo/test_unsupported.py 2010-04-17 17:41:17 +0000
@@ -35,15 +35,7 @@
3535
36class TestNoColocatedSupport(TestCaseWithBzrDir):36class TestNoColocatedSupport(TestCaseWithBzrDir):
3737
38 def test_destroy_colocated_branch(self):38 def make_bzrdir_with_repo(self):
39 branch = self.make_branch('branch')
40 # Colocated branches should not be supported *or*
41 # destroy_branch should not be supported at all
42 self.assertRaises(
43 (errors.NoColocatedBranchSupport, errors.UnsupportedOperation),
44 branch.bzrdir.destroy_branch, 'colo')
45
46 def test_create_colo_branch(self):
47 # a bzrdir can construct a branch and repository for itself.39 # a bzrdir can construct a branch and repository for itself.
48 if not self.bzrdir_format.is_supported():40 if not self.bzrdir_format.is_supported():
49 # unsupported formats are not loopback testable41 # unsupported formats are not loopback testable
@@ -53,7 +45,27 @@
53 t = get_transport(self.get_url())45 t = get_transport(self.get_url())
54 made_control = self.bzrdir_format.initialize(t.base)46 made_control = self.bzrdir_format.initialize(t.base)
55 made_repo = made_control.create_repository()47 made_repo = made_control.create_repository()
48 return made_control
49
50 def test_destroy_colocated_branch(self):
51 branch = self.make_branch('branch')
52 # Colocated branches should not be supported *or*
53 # destroy_branch should not be supported at all
54 self.assertRaises(
55 (errors.NoColocatedBranchSupport, errors.UnsupportedOperation),
56 branch.bzrdir.destroy_branch, 'colo')
57
58 def test_create_colo_branch(self):
59 made_control = self.make_bzrdir_with_repo()
56 self.assertRaises(errors.NoColocatedBranchSupport, 60 self.assertRaises(errors.NoColocatedBranchSupport,
57 made_control.create_branch, "colo")61 made_control.create_branch, "colo")
5862
63 def test_branch_transport(self):
64 made_control = self.make_bzrdir_with_repo()
65 self.assertRaises(errors.NoColocatedBranchSupport,
66 made_control.get_branch_transport, None, "colo")
5967
68 def test_get_branch_reference(self):
69 made_control = self.make_bzrdir_with_repo()
70 self.assertRaises(errors.NoColocatedBranchSupport,
71 made_control.get_branch_reference, "colo")
6072
=== modified file 'bzrlib/tests/test_branch.py'
--- bzrlib/tests/test_branch.py 2010-03-11 13:14:37 +0000
+++ bzrlib/tests/test_branch.py 2010-04-17 17:41:17 +0000
@@ -577,3 +577,40 @@
577 self.assertEqual(['lock_write', 'func called', 'unlock'], self._calls)577 self.assertEqual(['lock_write', 'func called', 'unlock'], self._calls)
578578
579579
580class TestExtractColocatedName(tests.TestCase):
581
582 def test_no_colocated(self):
583 self.assertEquals(("/some/path", None),
584 _mod_branch.split_colocated_name("/some/path"))
585
586 def test_colocated(self):
587 self.assertEquals(("/some/path", "tip"),
588 _mod_branch.split_colocated_name("/some/path,tip"))
589
590 def test_colocated_uses_right_comma(self):
591 self.assertEquals(("/some,dir/path", "tip"),
592 _mod_branch.split_colocated_name("/some,dir/path,tip"))
593
594 def test_colocated_allows_slashes(self):
595 self.assertEquals(("/somedir/path", "heads%2Ftip"),
596 _mod_branch.split_colocated_name("/somedir/path,heads%2Ftip"))
597
598 def test_none(self):
599 self.assertEquals((None, None),
600 _mod_branch.split_colocated_name(None))
601
602
603class TestJoinColocatedName(tests.TestCase):
604
605 def test_default(self):
606 self.assertEquals("/somedir/path",
607 _mod_branch.join_colocated_name("/somedir/path", None))
608
609 def test_colocated(self):
610 self.assertEquals("/somedir/path,brrr",
611 _mod_branch.join_colocated_name("/somedir/path", "brrr"))
612
613 def test_invalid_branch_name(self):
614 self.assertRaises(errors.InvalidColocatedBranchName,
615 _mod_branch.join_colocated_name, "/somedir/path", "brr,brr,brr")
616
580617
=== modified file 'bzrlib/tests/test_remote.py'
--- bzrlib/tests/test_remote.py 2010-02-23 07:43:11 +0000
+++ bzrlib/tests/test_remote.py 2010-04-17 17:41:17 +0000
@@ -584,7 +584,7 @@
584 # _get_tree_branch is a form of open_branch, but it should only ask for584 # _get_tree_branch is a form of open_branch, but it should only ask for
585 # branch opening, not any other network requests.585 # branch opening, not any other network requests.
586 calls = []586 calls = []
587 def open_branch():587 def open_branch(name=None):
588 calls.append("Called")588 calls.append("Called")
589 return "a-branch"589 return "a-branch"
590 transport = MemoryTransport()590 transport = MemoryTransport()
591591
=== modified file 'bzrlib/tests/test_urlutils.py'
--- bzrlib/tests/test_urlutils.py 2010-02-17 17:11:16 +0000
+++ bzrlib/tests/test_urlutils.py 2010-04-17 17:41:17 +0000
@@ -270,6 +270,17 @@
270 self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '..')270 self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '..')
271 self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '/..')271 self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '/..')
272272
273 def test_join_subsegments(self):
274 join_subsegments = urlutils.join_subsegments
275 self.assertEquals("/somedir/path",
276 join_subsegments("/somedir/path"))
277 self.assertEquals("/somedir/path,brrr",
278 join_subsegments("/somedir/path", "brrr"))
279 self.assertRaises(InvalidURLJoin,
280 join_subsegments, "/somedir/path", "brr,brr,brr")
281 self.assertEquals("/somedir/path,bla,bar",
282 join_subsegments("/somedir/path", "bla", "bar"))
283
273 def test_function_type(self):284 def test_function_type(self):
274 if sys.platform == 'win32':285 if sys.platform == 'win32':
275 self.assertEqual(urlutils._win32_local_path_to_url, urlutils.local_path_to_url)286 self.assertEqual(urlutils._win32_local_path_to_url, urlutils.local_path_to_url)
@@ -412,6 +423,19 @@
412 self.assertEqual(('path/..', 'foo'), split('path/../foo'))423 self.assertEqual(('path/..', 'foo'), split('path/../foo'))
413 self.assertEqual(('../path', 'foo'), split('../path/foo'))424 self.assertEqual(('../path', 'foo'), split('../path/foo'))
414425
426 def test_split_subsegments(self):
427 split_subsegments = urlutils.split_subsegments
428 self.assertEquals(("/some/path", []),
429 split_subsegments("/some/path"))
430 self.assertEquals(("/some/path", ["tip"]),
431 split_subsegments("/some/path,tip"))
432 self.assertEquals(("/some,dir/path", ["tip"]),
433 split_subsegments("/some,dir/path,tip"))
434 self.assertEquals(("/somedir/path", ["heads%2Ftip"]),
435 split_subsegments("/somedir/path,heads%2Ftip"))
436 self.assertEquals(("/somedir/path", ["heads%2Ftip", "bar"]),
437 split_subsegments("/somedir/path,heads%2Ftip,bar"))
438
415 def test_win32_strip_local_trailing_slash(self):439 def test_win32_strip_local_trailing_slash(self):
416 strip = urlutils._win32_strip_local_trailing_slash440 strip = urlutils._win32_strip_local_trailing_slash
417 self.assertEqual('file://', strip('file://'))441 self.assertEqual('file://', strip('file://'))
418442
=== modified file 'bzrlib/urlutils.py'
--- bzrlib/urlutils.py 2010-02-17 17:11:16 +0000
+++ bzrlib/urlutils.py 2010-04-17 17:41:17 +0000
@@ -469,6 +469,32 @@
469 return url_base + head, tail469 return url_base + head, tail
470470
471471
472def split_subsegments(url):
473 """Split the subsegment of the last segment of a URL.
474
475 :param url: A relative or absolute URL
476 :return: (url, subsegments)
477 """
478 (parent_url, child_dir) = split(url)
479 subsegments = child_dir.split(",")
480 if len(subsegments) == 1:
481 return (url, [])
482 return (join(parent_url, subsegments[0]), subsegments[1:])
483
484
485def join_subsegments(base, *subsegments):
486 """Create a new URL by adding subsegments to an existing one.
487
488 """
489 if not subsegments:
490 return base
491 for subsegment in subsegments:
492 if "," in subsegment:
493 raise errors.InvalidURLJoin(", exists in subsegments",
494 base, subsegments)
495 return ",".join((base,) + subsegments)
496
497
472def _win32_strip_local_trailing_slash(url):498def _win32_strip_local_trailing_slash(url):
473 """Strip slashes after the drive letter"""499 """Strip slashes after the drive letter"""
474 if len(url) > WIN32_MIN_ABS_FILEURL_LENGTH:500 if len(url) > WIN32_MIN_ABS_FILEURL_LENGTH: