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 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Robert Collins (community) | Needs Fixing | ||
Andrew Bennetts | Needs Information | ||
Review via email: mp+20860@code.launchpad.net |
Commit message
Description of the change
Add support for addressing colocated branches using comma's.
Jelmer Vernooij (jelmer) wrote : | # |
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://
* branch location: http://
* branch name: branchname
* http://
* branch location: http://
* branch name: branch,name
* http://
* branch location: http://
* 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/
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.
+ this_branch.
+ 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?
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.
Vincent Ladeuil (vila) wrote : | # |
@Robert: Did you unpack your comb ? :-)
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.
Robert Collins (lifeless) wrote : | # |
Am looking at this, iz not forgotten.
Robert Collins (lifeless) wrote : | # |
Oh, and @spiv - yes, , should be per segment in the path, not whole-string - thats the point of this ;)
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.
- 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).
Preview Diff
1 | === modified file 'bzrlib/branch.py' | |||
2 | --- bzrlib/branch.py 2010-04-10 18:03:54 +0000 | |||
3 | +++ bzrlib/branch.py 2010-04-17 17:41:17 +0000 | |||
4 | @@ -63,6 +63,34 @@ | |||
5 | 63 | BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n" | 63 | BZR_BRANCH_FORMAT_6 = "Bazaar Branch Format 6 (bzr 0.15)\n" |
6 | 64 | 64 | ||
7 | 65 | 65 | ||
8 | 66 | def split_colocated_name(url): | ||
9 | 67 | """Extract the colocated branch name from a URL. | ||
10 | 68 | |||
11 | 69 | :param url: The original URL | ||
12 | 70 | :return: Tuple with new URL and colocated branch name (None if not specified) | ||
13 | 71 | """ | ||
14 | 72 | if url is None: | ||
15 | 73 | return (None, None) | ||
16 | 74 | (base, subsegments) = urlutils.split_subsegments(url) | ||
17 | 75 | if len(subsegments) == 0: | ||
18 | 76 | return (url, None) | ||
19 | 77 | if len(subsegments) > 1: | ||
20 | 78 | raise errors.InvalidURL(url) | ||
21 | 79 | return (base, subsegments[0]) | ||
22 | 80 | |||
23 | 81 | |||
24 | 82 | def join_colocated_name(base, branch_name): | ||
25 | 83 | """Combine a location and a colocated branch name URL. | ||
26 | 84 | |||
27 | 85 | :param base: Base location | ||
28 | 86 | :param branch_name: Co-located branch name | ||
29 | 87 | :return: A URL that addresses a colocated branch | ||
30 | 88 | """ | ||
31 | 89 | if branch_name is None: | ||
32 | 90 | return base | ||
33 | 91 | return urlutils.join_subsegments(base, branch_name) | ||
34 | 92 | |||
35 | 93 | |||
36 | 66 | # TODO: Maybe include checks for common corruption of newlines, etc? | 94 | # TODO: Maybe include checks for common corruption of newlines, etc? |
37 | 67 | 95 | ||
38 | 68 | # TODO: Some operations like log might retrieve the same revisions | 96 | # TODO: Some operations like log might retrieve the same revisions |
39 | @@ -159,15 +187,16 @@ | |||
40 | 159 | return [('revision-existence', revid), ('lefthand-distance', revid)] | 187 | return [('revision-existence', revid), ('lefthand-distance', revid)] |
41 | 160 | 188 | ||
42 | 161 | @staticmethod | 189 | @staticmethod |
44 | 162 | def open(base, _unsupported=False, possible_transports=None): | 190 | def open(url, _unsupported=False, possible_transports=None): |
45 | 163 | """Open the branch rooted at base. | 191 | """Open the branch rooted at base. |
46 | 164 | 192 | ||
47 | 165 | For instance, if the branch is at URL/.bzr/branch, | 193 | For instance, if the branch is at URL/.bzr/branch, |
48 | 166 | Branch.open(URL) -> a Branch instance. | 194 | Branch.open(URL) -> a Branch instance. |
49 | 167 | """ | 195 | """ |
50 | 196 | (base, branch_name) = split_colocated_name(url) | ||
51 | 168 | control = bzrdir.BzrDir.open(base, _unsupported, | 197 | control = bzrdir.BzrDir.open(base, _unsupported, |
52 | 169 | possible_transports=possible_transports) | 198 | possible_transports=possible_transports) |
54 | 170 | return control.open_branch(unsupported=_unsupported) | 199 | return control.open_branch(name=branch_name, unsupported=_unsupported) |
55 | 171 | 200 | ||
56 | 172 | @staticmethod | 201 | @staticmethod |
57 | 173 | def open_from_transport(transport, name=None, _unsupported=False): | 202 | def open_from_transport(transport, name=None, _unsupported=False): |
58 | @@ -187,9 +216,10 @@ | |||
59 | 187 | format, UnknownFormatError or UnsupportedFormatError are raised. | 216 | format, UnknownFormatError or UnsupportedFormatError are raised. |
60 | 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. |
61 | 189 | """ | 218 | """ |
63 | 190 | control, relpath = bzrdir.BzrDir.open_containing(url, | 219 | (base, branch_name) = split_colocated_name(url) |
64 | 220 | control, relpath = bzrdir.BzrDir.open_containing(base, | ||
65 | 191 | possible_transports) | 221 | possible_transports) |
67 | 192 | return control.open_branch(), relpath | 222 | return control.open_branch(name=branch_name), relpath |
68 | 193 | 223 | ||
69 | 194 | def _push_should_merge_tags(self): | 224 | def _push_should_merge_tags(self): |
70 | 195 | """Should _basic_push merge this branch's tags into the target? | 225 | """Should _basic_push merge this branch's tags into the target? |
71 | @@ -1335,6 +1365,8 @@ | |||
72 | 1335 | """ | 1365 | """ |
73 | 1336 | # XXX: Fix the bzrdir API to allow getting the branch back from the | 1366 | # XXX: Fix the bzrdir API to allow getting the branch back from the |
74 | 1337 | # clone call. Or something. 20090224 RBC/spiv. | 1367 | # clone call. Or something. 20090224 RBC/spiv. |
75 | 1368 | # XXX: Should this perhaps clone colocated branches as well, | ||
76 | 1369 | # rather than just the default branch? 20100319 JRV | ||
77 | 1338 | if revision_id is None: | 1370 | if revision_id is None: |
78 | 1339 | revision_id = self.last_revision() | 1371 | revision_id = self.last_revision() |
79 | 1340 | dir_to = self.bzrdir.clone_on_transport(to_transport, | 1372 | dir_to = self.bzrdir.clone_on_transport(to_transport, |
80 | @@ -1510,7 +1542,7 @@ | |||
81 | 1510 | """Return the current default format.""" | 1542 | """Return the current default format.""" |
82 | 1511 | return klass._default_format | 1543 | return klass._default_format |
83 | 1512 | 1544 | ||
85 | 1513 | def get_reference(self, a_bzrdir): | 1545 | def get_reference(self, a_bzrdir, branch_name=None): |
86 | 1514 | """Get the target reference of the branch in a_bzrdir. | 1546 | """Get the target reference of the branch in a_bzrdir. |
87 | 1515 | 1547 | ||
88 | 1516 | format probing must have been completed before calling | 1548 | format probing must have been completed before calling |
89 | @@ -1518,12 +1550,13 @@ | |||
90 | 1518 | in a_bzrdir is correct. | 1550 | in a_bzrdir is correct. |
91 | 1519 | 1551 | ||
92 | 1520 | :param a_bzrdir: The bzrdir to get the branch data from. | 1552 | :param a_bzrdir: The bzrdir to get the branch data from. |
93 | 1553 | :param branch_name: Name of the colocated branch to fetch | ||
94 | 1521 | :return: None if the branch is not a reference branch. | 1554 | :return: None if the branch is not a reference branch. |
95 | 1522 | """ | 1555 | """ |
96 | 1523 | return None | 1556 | return None |
97 | 1524 | 1557 | ||
98 | 1525 | @classmethod | 1558 | @classmethod |
100 | 1526 | def set_reference(self, a_bzrdir, to_branch): | 1559 | def set_reference(self, a_bzrdir, branch_name, to_branch): |
101 | 1527 | """Set the target reference of the branch in a_bzrdir. | 1560 | """Set the target reference of the branch in a_bzrdir. |
102 | 1528 | 1561 | ||
103 | 1529 | format probing must have been completed before calling | 1562 | format probing must have been completed before calling |
104 | @@ -1531,6 +1564,7 @@ | |||
105 | 1531 | in a_bzrdir is correct. | 1564 | in a_bzrdir is correct. |
106 | 1532 | 1565 | ||
107 | 1533 | :param a_bzrdir: The bzrdir to set the branch reference for. | 1566 | :param a_bzrdir: The bzrdir to set the branch reference for. |
108 | 1567 | :param branch_name: Name of colocated branch to set, None for default | ||
109 | 1534 | :param to_branch: branch that the checkout is to reference | 1568 | :param to_branch: branch that the checkout is to reference |
110 | 1535 | """ | 1569 | """ |
111 | 1536 | raise NotImplementedError(self.set_reference) | 1570 | raise NotImplementedError(self.set_reference) |
112 | @@ -2048,14 +2082,14 @@ | |||
113 | 2048 | """See BranchFormat.get_format_description().""" | 2082 | """See BranchFormat.get_format_description().""" |
114 | 2049 | return "Checkout reference format 1" | 2083 | return "Checkout reference format 1" |
115 | 2050 | 2084 | ||
117 | 2051 | def get_reference(self, a_bzrdir): | 2085 | def get_reference(self, a_bzrdir, branch_name=None): |
118 | 2052 | """See BranchFormat.get_reference().""" | 2086 | """See BranchFormat.get_reference().""" |
120 | 2053 | transport = a_bzrdir.get_branch_transport(None) | 2087 | transport = a_bzrdir.get_branch_transport(branch_name) |
121 | 2054 | return transport.get_bytes('location') | 2088 | return transport.get_bytes('location') |
122 | 2055 | 2089 | ||
124 | 2056 | def set_reference(self, a_bzrdir, to_branch): | 2090 | def set_reference(self, a_bzrdir, branch_name, to_branch): |
125 | 2057 | """See BranchFormat.set_reference().""" | 2091 | """See BranchFormat.set_reference().""" |
127 | 2058 | transport = a_bzrdir.get_branch_transport(None) | 2092 | transport = a_bzrdir.get_branch_transport(branch_name) |
128 | 2059 | location = transport.put_bytes('location', to_branch.base) | 2093 | location = transport.put_bytes('location', to_branch.base) |
129 | 2060 | 2094 | ||
130 | 2061 | def initialize(self, a_bzrdir, name=None, target_branch=None): | 2095 | def initialize(self, a_bzrdir, name=None, target_branch=None): |
131 | @@ -2110,7 +2144,7 @@ | |||
132 | 2110 | raise AssertionError("wrong format %r found for %r" % | 2144 | raise AssertionError("wrong format %r found for %r" % |
133 | 2111 | (format, self)) | 2145 | (format, self)) |
134 | 2112 | if location is None: | 2146 | if location is None: |
136 | 2113 | location = self.get_reference(a_bzrdir) | 2147 | location = self.get_reference(a_bzrdir, name) |
137 | 2114 | real_bzrdir = bzrdir.BzrDir.open( | 2148 | real_bzrdir = bzrdir.BzrDir.open( |
138 | 2115 | location, possible_transports=possible_transports) | 2149 | location, possible_transports=possible_transports) |
139 | 2116 | result = real_bzrdir.open_branch(name=name, | 2150 | result = real_bzrdir.open_branch(name=name, |
140 | 2117 | 2151 | ||
141 | === modified file 'bzrlib/builtins.py' | |||
142 | --- bzrlib/builtins.py 2010-04-15 15:03:15 +0000 | |||
143 | +++ bzrlib/builtins.py 2010-04-17 17:41:17 +0000 | |||
144 | @@ -52,7 +52,11 @@ | |||
145 | 52 | urlutils, | 52 | urlutils, |
146 | 53 | views, | 53 | views, |
147 | 54 | ) | 54 | ) |
149 | 55 | from bzrlib.branch import Branch | 55 | from bzrlib.branch import ( |
150 | 56 | Branch, | ||
151 | 57 | join_colocated_name, | ||
152 | 58 | split_colocated_name, | ||
153 | 59 | ) | ||
154 | 56 | from bzrlib.conflicts import ConflictList | 60 | from bzrlib.conflicts import ConflictList |
155 | 57 | from bzrlib.transport import memory | 61 | from bzrlib.transport import memory |
156 | 58 | from bzrlib.revisionspec import RevisionSpec, RevisionInfo | 62 | from bzrlib.revisionspec import RevisionSpec, RevisionInfo |
157 | @@ -1230,23 +1234,24 @@ | |||
158 | 1230 | revision_id = br_from.last_revision() | 1234 | revision_id = br_from.last_revision() |
159 | 1231 | if to_location is None: | 1235 | if to_location is None: |
160 | 1232 | to_location = urlutils.derive_to_location(from_location) | 1236 | to_location = urlutils.derive_to_location(from_location) |
162 | 1233 | to_transport = transport.get_transport(to_location) | 1237 | to_base, to_branch_name = split_colocated_name(to_location) |
163 | 1238 | to_transport = transport.get_transport(to_base) | ||
164 | 1234 | try: | 1239 | try: |
165 | 1235 | to_transport.mkdir('.') | 1240 | to_transport.mkdir('.') |
166 | 1236 | except errors.FileExists: | 1241 | except errors.FileExists: |
167 | 1237 | if not use_existing_dir: | 1242 | if not use_existing_dir: |
168 | 1238 | raise errors.BzrCommandError('Target directory "%s" ' | 1243 | raise errors.BzrCommandError('Target directory "%s" ' |
170 | 1239 | 'already exists.' % to_location) | 1244 | 'already exists.' % to_base) |
171 | 1240 | else: | 1245 | else: |
172 | 1241 | try: | 1246 | try: |
173 | 1242 | bzrdir.BzrDir.open_from_transport(to_transport) | 1247 | bzrdir.BzrDir.open_from_transport(to_transport) |
174 | 1243 | except errors.NotBranchError: | 1248 | except errors.NotBranchError: |
175 | 1244 | pass | 1249 | pass |
176 | 1245 | else: | 1250 | else: |
178 | 1246 | raise errors.AlreadyBranchError(to_location) | 1251 | raise errors.AlreadyBranchError(to_base) |
179 | 1247 | except errors.NoSuchFile: | 1252 | except errors.NoSuchFile: |
180 | 1248 | raise errors.BzrCommandError('Parent of "%s" does not exist.' | 1253 | raise errors.BzrCommandError('Parent of "%s" does not exist.' |
182 | 1249 | % to_location) | 1254 | % to_base) |
183 | 1250 | try: | 1255 | try: |
184 | 1251 | # preserve whatever source format we have. | 1256 | # preserve whatever source format we have. |
185 | 1252 | dir = br_from.bzrdir.sprout(to_transport.base, revision_id, | 1257 | dir = br_from.bzrdir.sprout(to_transport.base, revision_id, |
186 | @@ -1256,7 +1261,7 @@ | |||
187 | 1256 | force_new_repo=standalone, | 1261 | force_new_repo=standalone, |
188 | 1257 | create_tree_if_local=not no_tree, | 1262 | create_tree_if_local=not no_tree, |
189 | 1258 | source_branch=br_from) | 1263 | source_branch=br_from) |
191 | 1259 | branch = dir.open_branch() | 1264 | branch = dir.open_branch(to_branch_name) |
192 | 1260 | except errors.NoSuchRevision: | 1265 | except errors.NoSuchRevision: |
193 | 1261 | to_transport.delete_tree('.') | 1266 | to_transport.delete_tree('.') |
194 | 1262 | msg = "The branch %s has no revision %s." % (from_location, | 1267 | msg = "The branch %s has no revision %s." % (from_location, |
195 | @@ -1731,7 +1736,8 @@ | |||
196 | 1731 | if location is None: | 1736 | if location is None: |
197 | 1732 | location = u'.' | 1737 | location = u'.' |
198 | 1733 | 1738 | ||
200 | 1734 | to_transport = transport.get_transport(location) | 1739 | (base, branch_name) = split_colocated_name(location) |
201 | 1740 | to_transport = transport.get_transport(base) | ||
202 | 1735 | 1741 | ||
203 | 1736 | # The path has to exist to initialize a | 1742 | # The path has to exist to initialize a |
204 | 1737 | # branch inside of it. | 1743 | # branch inside of it. |
205 | @@ -1746,7 +1752,7 @@ | |||
206 | 1746 | " does not exist." | 1752 | " does not exist." |
207 | 1747 | "\nYou may supply --create-prefix to create all" | 1753 | "\nYou may supply --create-prefix to create all" |
208 | 1748 | " leading parent directories." | 1754 | " leading parent directories." |
210 | 1749 | % location) | 1755 | % base) |
211 | 1750 | to_transport.create_prefix() | 1756 | to_transport.create_prefix() |
212 | 1751 | 1757 | ||
213 | 1752 | try: | 1758 | try: |
214 | @@ -1754,17 +1760,18 @@ | |||
215 | 1754 | except errors.NotBranchError: | 1760 | except errors.NotBranchError: |
216 | 1755 | # really a NotBzrDir error... | 1761 | # really a NotBzrDir error... |
217 | 1756 | create_branch = bzrdir.BzrDir.create_branch_convenience | 1762 | create_branch = bzrdir.BzrDir.create_branch_convenience |
220 | 1757 | branch = create_branch(to_transport.base, format=format, | 1763 | branch = create_branch( |
221 | 1758 | possible_transports=[to_transport]) | 1764 | join_colocated_name(to_transport.base, branch_name), |
222 | 1765 | format=format, possible_transports=[to_transport]) | ||
223 | 1759 | a_bzrdir = branch.bzrdir | 1766 | a_bzrdir = branch.bzrdir |
224 | 1760 | else: | 1767 | else: |
225 | 1761 | from bzrlib.transport.local import LocalTransport | 1768 | from bzrlib.transport.local import LocalTransport |
227 | 1762 | if a_bzrdir.has_branch(): | 1769 | if a_bzrdir.has_branch(branch_name): |
228 | 1763 | if (isinstance(to_transport, LocalTransport) | 1770 | if (isinstance(to_transport, LocalTransport) |
229 | 1764 | and not a_bzrdir.has_workingtree()): | 1771 | and not a_bzrdir.has_workingtree()): |
230 | 1765 | raise errors.BranchExistsWithoutWorkingTree(location) | 1772 | raise errors.BranchExistsWithoutWorkingTree(location) |
231 | 1766 | raise errors.AlreadyBranchError(location) | 1773 | raise errors.AlreadyBranchError(location) |
233 | 1767 | branch = a_bzrdir.create_branch() | 1774 | branch = a_bzrdir.create_branch(branch_name) |
234 | 1768 | a_bzrdir.create_workingtree() | 1775 | a_bzrdir.create_workingtree() |
235 | 1769 | if append_revisions_only: | 1776 | if append_revisions_only: |
236 | 1770 | try: | 1777 | try: |
237 | @@ -2365,8 +2372,7 @@ | |||
238 | 2365 | location = revision[0].get_branch() | 2372 | location = revision[0].get_branch() |
239 | 2366 | else: | 2373 | else: |
240 | 2367 | location = '.' | 2374 | location = '.' |
243 | 2368 | dir, relpath = bzrdir.BzrDir.open_containing(location) | 2375 | b, relpath = Branch.open_containing(location) |
242 | 2369 | b = dir.open_branch() | ||
244 | 2370 | b.lock_read() | 2376 | b.lock_read() |
245 | 2371 | self.add_cleanup(b.unlock) | 2377 | self.add_cleanup(b.unlock) |
246 | 2372 | rev1, rev2 = _get_revision_range(revision, b, self.name()) | 2378 | rev1, rev2 = _get_revision_range(revision, b, self.name()) |
247 | @@ -4732,13 +4738,8 @@ | |||
248 | 4732 | revision=None, force=False, local=False): | 4738 | revision=None, force=False, local=False): |
249 | 4733 | if location is None: | 4739 | if location is None: |
250 | 4734 | location = u'.' | 4740 | location = u'.' |
258 | 4735 | control, relpath = bzrdir.BzrDir.open_containing(location) | 4741 | tree, b, relpath = bzrdir.BzrDir.open_containing_tree_or_branch( |
259 | 4736 | try: | 4742 | location) |
253 | 4737 | tree = control.open_workingtree() | ||
254 | 4738 | b = tree.branch | ||
255 | 4739 | except (errors.NoWorkingTree, errors.NotLocalUrl): | ||
256 | 4740 | tree = None | ||
257 | 4741 | b = control.open_branch() | ||
260 | 4742 | 4743 | ||
261 | 4743 | if tree is not None: | 4744 | if tree is not None: |
262 | 4744 | tree.lock_write() | 4745 | tree.lock_write() |
263 | @@ -5543,51 +5544,53 @@ | |||
264 | 5543 | from bzrlib import switch | 5544 | from bzrlib import switch |
265 | 5544 | tree_location = '.' | 5545 | tree_location = '.' |
266 | 5545 | revision = _get_one_revision('switch', revision) | 5546 | revision = _get_one_revision('switch', revision) |
268 | 5546 | control_dir = bzrdir.BzrDir.open_containing(tree_location)[0] | 5547 | tree_base, branch_name = split_colocated_name(tree_location) |
269 | 5548 | control_dir = bzrdir.BzrDir.open_containing(tree_base)[0] | ||
270 | 5547 | if to_location is None: | 5549 | if to_location is None: |
271 | 5548 | if revision is None: | 5550 | if revision is None: |
272 | 5549 | raise errors.BzrCommandError('You must supply either a' | 5551 | raise errors.BzrCommandError('You must supply either a' |
273 | 5550 | ' revision or a location') | 5552 | ' revision or a location') |
274 | 5551 | to_location = '.' | 5553 | to_location = '.' |
275 | 5552 | try: | 5554 | try: |
277 | 5553 | branch = control_dir.open_branch() | 5555 | branch = control_dir.open_branch(branch_name) |
278 | 5554 | had_explicit_nick = branch.get_config().has_explicit_nickname() | 5556 | had_explicit_nick = branch.get_config().has_explicit_nickname() |
279 | 5555 | except errors.NotBranchError: | 5557 | except errors.NotBranchError: |
280 | 5556 | branch = None | 5558 | branch = None |
281 | 5557 | had_explicit_nick = False | 5559 | had_explicit_nick = False |
282 | 5560 | to_base, to_branch_name = split_colocated_name(to_location) | ||
283 | 5558 | if create_branch: | 5561 | if create_branch: |
284 | 5559 | if branch is None: | 5562 | if branch is None: |
285 | 5560 | raise errors.BzrCommandError('cannot create branch without' | 5563 | raise errors.BzrCommandError('cannot create branch without' |
286 | 5561 | ' source branch') | 5564 | ' source branch') |
290 | 5562 | to_location = directory_service.directories.dereference( | 5565 | to_base = directory_service.directories.dereference( |
291 | 5563 | to_location) | 5566 | to_base) |
292 | 5564 | if '/' not in to_location and '\\' not in to_location: | 5567 | if '/' not in to_base and '\\' not in to_base: |
293 | 5565 | # This path is meant to be relative to the existing branch | 5568 | # This path is meant to be relative to the existing branch |
294 | 5566 | this_url = self._get_branch_location(control_dir) | 5569 | this_url = self._get_branch_location(control_dir) |
299 | 5567 | to_location = urlutils.join(this_url, '..', to_location) | 5570 | to_base = urlutils.join(this_url, '..', to_base) |
300 | 5568 | to_branch = branch.bzrdir.sprout(to_location, | 5571 | to_branch = branch.bzrdir.sprout(to_base, |
301 | 5569 | possible_transports=[branch.bzrdir.root_transport], | 5572 | possible_transports=[branch.bzrdir.root_transport], |
302 | 5570 | source_branch=branch).open_branch() | 5573 | source_branch=branch).open_branch(to_branch_name) |
303 | 5571 | else: | 5574 | else: |
304 | 5572 | try: | 5575 | try: |
305 | 5573 | to_branch = Branch.open(to_location) | 5576 | to_branch = Branch.open(to_location) |
306 | 5574 | except errors.NotBranchError: | 5577 | except errors.NotBranchError: |
308 | 5575 | this_url = self._get_branch_location(control_dir) | 5578 | this_url = self._get_branch_location(control_dir, branch_name) |
309 | 5576 | to_branch = Branch.open( | 5579 | to_branch = Branch.open( |
310 | 5577 | urlutils.join(this_url, '..', to_location)) | 5580 | urlutils.join(this_url, '..', to_location)) |
311 | 5578 | if revision is not None: | 5581 | if revision is not None: |
312 | 5579 | revision = revision.as_revision_id(to_branch) | 5582 | revision = revision.as_revision_id(to_branch) |
313 | 5580 | switch.switch(control_dir, to_branch, force, revision_id=revision) | 5583 | switch.switch(control_dir, to_branch, force, revision_id=revision) |
314 | 5581 | if had_explicit_nick: | 5584 | if had_explicit_nick: |
316 | 5582 | branch = control_dir.open_branch() #get the new branch! | 5585 | branch = control_dir.open_branch(to_branch_name) #get the new branch! |
317 | 5583 | branch.nick = to_branch.nick | 5586 | branch.nick = to_branch.nick |
318 | 5584 | note('Switched to branch: %s', | 5587 | note('Switched to branch: %s', |
319 | 5585 | urlutils.unescape_for_display(to_branch.base, 'utf-8')) | 5588 | urlutils.unescape_for_display(to_branch.base, 'utf-8')) |
320 | 5586 | 5589 | ||
322 | 5587 | def _get_branch_location(self, control_dir): | 5590 | def _get_branch_location(self, control_dir, branch_name): |
323 | 5588 | """Return location of branch for this control dir.""" | 5591 | """Return location of branch for this control dir.""" |
324 | 5589 | try: | 5592 | try: |
326 | 5590 | this_branch = control_dir.open_branch() | 5593 | this_branch = control_dir.open_branch(branch_name) |
327 | 5591 | # This may be a heavy checkout, where we want the master branch | 5594 | # This may be a heavy checkout, where we want the master branch |
328 | 5592 | master_location = this_branch.get_bound_location() | 5595 | master_location = this_branch.get_bound_location() |
329 | 5593 | if master_location is not None: | 5596 | if master_location is not None: |
330 | @@ -5597,7 +5600,7 @@ | |||
331 | 5597 | except errors.NotBranchError: | 5600 | except errors.NotBranchError: |
332 | 5598 | format = control_dir.find_branch_format() | 5601 | format = control_dir.find_branch_format() |
333 | 5599 | if getattr(format, 'get_reference', None) is not None: | 5602 | if getattr(format, 'get_reference', None) is not None: |
335 | 5600 | return format.get_reference(control_dir) | 5603 | return format.get_reference(control_dir, branch_name) |
336 | 5601 | else: | 5604 | else: |
337 | 5602 | return control_dir.root_transport.base | 5605 | return control_dir.root_transport.base |
338 | 5603 | 5606 | ||
339 | 5604 | 5607 | ||
340 | === modified file 'bzrlib/bzrdir.py' | |||
341 | --- bzrlib/bzrdir.py 2010-04-15 15:03:15 +0000 | |||
342 | +++ bzrlib/bzrdir.py 2010-04-17 17:41:17 +0000 | |||
343 | @@ -220,7 +220,7 @@ | |||
344 | 220 | except errors.NoRepositoryPresent: | 220 | except errors.NoRepositoryPresent: |
345 | 221 | local_repo = None | 221 | local_repo = None |
346 | 222 | try: | 222 | try: |
348 | 223 | local_branch = self.open_branch() | 223 | local_branch = self.open_branch() # XXX Deal with colocated branches |
349 | 224 | except errors.NotBranchError: | 224 | except errors.NotBranchError: |
350 | 225 | local_branch = None | 225 | local_branch = None |
351 | 226 | else: | 226 | else: |
352 | @@ -704,13 +704,18 @@ | |||
353 | 704 | raise errors.NoRepositoryPresent(self) | 704 | raise errors.NoRepositoryPresent(self) |
354 | 705 | return found_repo | 705 | return found_repo |
355 | 706 | 706 | ||
357 | 707 | def get_branch_reference(self): | 707 | def get_branch_reference(self, name=None): |
358 | 708 | """Return the referenced URL for the branch in this bzrdir. | 708 | """Return the referenced URL for the branch in this bzrdir. |
359 | 709 | 709 | ||
360 | 710 | :param name: Optional colocated branch name | ||
361 | 710 | :raises NotBranchError: If there is no Branch. | 711 | :raises NotBranchError: If there is no Branch. |
362 | 712 | :raises NoColocatedBranchSupport: If a branch name was specified | ||
363 | 713 | but colocated branches are not supported. | ||
364 | 711 | :return: The URL the branch in this bzrdir references if it is a | 714 | :return: The URL the branch in this bzrdir references if it is a |
365 | 712 | reference branch, or None for regular branches. | 715 | reference branch, or None for regular branches. |
366 | 713 | """ | 716 | """ |
367 | 717 | if name is not None: | ||
368 | 718 | raise errors.NoColocatedBranchSupport(self) | ||
369 | 714 | return None | 719 | return None |
370 | 715 | 720 | ||
371 | 716 | def get_branch_transport(self, branch_format, name=None): | 721 | def get_branch_transport(self, branch_format, name=None): |
372 | @@ -951,9 +956,11 @@ | |||
373 | 951 | raise errors.NotBranchError(path=url) | 956 | raise errors.NotBranchError(path=url) |
374 | 952 | a_transport = new_t | 957 | a_transport = new_t |
375 | 953 | 958 | ||
377 | 954 | def _get_tree_branch(self): | 959 | def _get_tree_branch(self, name=None): |
378 | 955 | """Return the branch and tree, if any, for this bzrdir. | 960 | """Return the branch and tree, if any, for this bzrdir. |
379 | 956 | 961 | ||
380 | 962 | :param name: Name of colocated branch to open. | ||
381 | 963 | |||
382 | 957 | Return None for tree if not present or inaccessible. | 964 | Return None for tree if not present or inaccessible. |
383 | 958 | Raise NotBranchError if no branch is present. | 965 | Raise NotBranchError if no branch is present. |
384 | 959 | :return: (tree, branch) | 966 | :return: (tree, branch) |
385 | @@ -962,9 +969,12 @@ | |||
386 | 962 | tree = self.open_workingtree() | 969 | tree = self.open_workingtree() |
387 | 963 | except (errors.NoWorkingTree, errors.NotLocalUrl): | 970 | except (errors.NoWorkingTree, errors.NotLocalUrl): |
388 | 964 | tree = None | 971 | tree = None |
390 | 965 | branch = self.open_branch() | 972 | branch = self.open_branch(name=name) |
391 | 966 | else: | 973 | else: |
393 | 967 | branch = tree.branch | 974 | if name is not None: |
394 | 975 | branch = self.open_branch(name=name) | ||
395 | 976 | else: | ||
396 | 977 | branch = tree.branch | ||
397 | 968 | return tree, branch | 978 | return tree, branch |
398 | 969 | 979 | ||
399 | 970 | @classmethod | 980 | @classmethod |
400 | @@ -976,8 +986,10 @@ | |||
401 | 976 | raised | 986 | raised |
402 | 977 | :return: (tree, branch) | 987 | :return: (tree, branch) |
403 | 978 | """ | 988 | """ |
404 | 989 | from bzrlib.branch import split_colocated_name | ||
405 | 990 | (location, branch_name) = split_colocated_name(location) | ||
406 | 979 | bzrdir = klass.open(location) | 991 | bzrdir = klass.open(location) |
408 | 980 | return bzrdir._get_tree_branch() | 992 | return bzrdir._get_tree_branch(branch_name) |
409 | 981 | 993 | ||
410 | 982 | @classmethod | 994 | @classmethod |
411 | 983 | def open_containing_tree_or_branch(klass, location): | 995 | def open_containing_tree_or_branch(klass, location): |
412 | @@ -989,8 +1001,10 @@ | |||
413 | 989 | raised | 1001 | raised |
414 | 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. |
415 | 991 | """ | 1003 | """ |
416 | 1004 | from bzrlib.branch import split_colocated_name | ||
417 | 1005 | (location, branch_name) = split_colocated_name(location) | ||
418 | 992 | bzrdir, relpath = klass.open_containing(location) | 1006 | bzrdir, relpath = klass.open_containing(location) |
420 | 993 | tree, branch = bzrdir._get_tree_branch() | 1007 | tree, branch = bzrdir._get_tree_branch(branch_name) |
421 | 994 | return tree, branch, relpath | 1008 | return tree, branch, relpath |
422 | 995 | 1009 | ||
423 | 996 | @classmethod | 1010 | @classmethod |
424 | @@ -1261,13 +1275,13 @@ | |||
425 | 1261 | return result | 1275 | return result |
426 | 1262 | 1276 | ||
427 | 1263 | def push_branch(self, source, revision_id=None, overwrite=False, | 1277 | def push_branch(self, source, revision_id=None, overwrite=False, |
429 | 1264 | remember=False, create_prefix=False): | 1278 | remember=False, create_prefix=False, branch_name=None): |
430 | 1265 | """Push the source branch into this BzrDir.""" | 1279 | """Push the source branch into this BzrDir.""" |
431 | 1266 | br_to = None | 1280 | br_to = None |
432 | 1267 | # If we can open a branch, use its direct repository, otherwise see | 1281 | # If we can open a branch, use its direct repository, otherwise see |
433 | 1268 | # if there is a repository without a branch. | 1282 | # if there is a repository without a branch. |
434 | 1269 | try: | 1283 | try: |
436 | 1270 | br_to = self.open_branch() | 1284 | br_to = self.open_branch(branch_name) |
437 | 1271 | except errors.NotBranchError: | 1285 | except errors.NotBranchError: |
438 | 1272 | # Didn't find a branch, can we find a repository? | 1286 | # Didn't find a branch, can we find a repository? |
439 | 1273 | repository_to = self.find_repository() | 1287 | repository_to = self.find_repository() |
440 | @@ -1652,13 +1666,13 @@ | |||
441 | 1652 | def destroy_workingtree_metadata(self): | 1666 | def destroy_workingtree_metadata(self): |
442 | 1653 | self.transport.delete_tree('checkout') | 1667 | self.transport.delete_tree('checkout') |
443 | 1654 | 1668 | ||
445 | 1655 | def find_branch_format(self): | 1669 | def find_branch_format(self, name=None): |
446 | 1656 | """Find the branch 'format' for this bzrdir. | 1670 | """Find the branch 'format' for this bzrdir. |
447 | 1657 | 1671 | ||
448 | 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. |
449 | 1659 | """ | 1673 | """ |
450 | 1660 | from bzrlib.branch import BranchFormat | 1674 | from bzrlib.branch import BranchFormat |
452 | 1661 | return BranchFormat.find_format(self) | 1675 | return BranchFormat.find_format(self, name=name) |
453 | 1662 | 1676 | ||
454 | 1663 | def _get_mkdir_mode(self): | 1677 | def _get_mkdir_mode(self): |
455 | 1664 | """Figure out the mode to use when creating a bzrdir subdir.""" | 1678 | """Figure out the mode to use when creating a bzrdir subdir.""" |
456 | @@ -1666,11 +1680,11 @@ | |||
457 | 1666 | lockable_files.TransportLock) | 1680 | lockable_files.TransportLock) |
458 | 1667 | return temp_control._dir_mode | 1681 | return temp_control._dir_mode |
459 | 1668 | 1682 | ||
461 | 1669 | def get_branch_reference(self): | 1683 | def get_branch_reference(self, name=None): |
462 | 1670 | """See BzrDir.get_branch_reference().""" | 1684 | """See BzrDir.get_branch_reference().""" |
463 | 1671 | from bzrlib.branch import BranchFormat | 1685 | from bzrlib.branch import BranchFormat |
466 | 1672 | format = BranchFormat.find_format(self) | 1686 | format = BranchFormat.find_format(self, name=name) |
467 | 1673 | return format.get_reference(self) | 1687 | return format.get_reference(self, name=name) |
468 | 1674 | 1688 | ||
469 | 1675 | def get_branch_transport(self, branch_format, name=None): | 1689 | def get_branch_transport(self, branch_format, name=None): |
470 | 1676 | """See BzrDir.get_branch_transport().""" | 1690 | """See BzrDir.get_branch_transport().""" |
471 | @@ -1770,7 +1784,7 @@ | |||
472 | 1770 | def open_branch(self, name=None, unsupported=False, | 1784 | def open_branch(self, name=None, unsupported=False, |
473 | 1771 | ignore_fallbacks=False): | 1785 | ignore_fallbacks=False): |
474 | 1772 | """See BzrDir.open_branch.""" | 1786 | """See BzrDir.open_branch.""" |
476 | 1773 | format = self.find_branch_format() | 1787 | format = self.find_branch_format(name=name) |
477 | 1774 | self._check_supported(format, unsupported) | 1788 | self._check_supported(format, unsupported) |
478 | 1775 | return format.open(self, name=name, | 1789 | return format.open(self, name=name, |
479 | 1776 | _found=True, ignore_fallbacks=ignore_fallbacks) | 1790 | _found=True, ignore_fallbacks=ignore_fallbacks) |
480 | 1777 | 1791 | ||
481 | === modified file 'bzrlib/errors.py' | |||
482 | --- bzrlib/errors.py 2010-03-24 14:15:01 +0000 | |||
483 | +++ bzrlib/errors.py 2010-04-17 17:41:17 +0000 | |||
484 | @@ -3134,3 +3134,10 @@ | |||
485 | 3134 | def __init__(self, bzrdir): | 3134 | def __init__(self, bzrdir): |
486 | 3135 | self.bzrdir = bzrdir | 3135 | self.bzrdir = bzrdir |
487 | 3136 | 3136 | ||
488 | 3137 | |||
489 | 3138 | class InvalidColocatedBranchName(BzrError): | ||
490 | 3139 | |||
491 | 3140 | _fmt = "%(branch_name)s is an invalid name for a colocated branch." | ||
492 | 3141 | |||
493 | 3142 | def __init__(self, branch_name): | ||
494 | 3143 | self.branch_name = branch_name | ||
495 | 3137 | 3144 | ||
496 | === modified file 'bzrlib/reconcile.py' | |||
497 | --- bzrlib/reconcile.py 2010-02-17 17:11:16 +0000 | |||
498 | +++ bzrlib/reconcile.py 2010-04-17 17:41:17 +0000 | |||
499 | @@ -80,19 +80,16 @@ | |||
500 | 80 | 80 | ||
501 | 81 | def _reconcile(self): | 81 | def _reconcile(self): |
502 | 82 | """Helper function for performing reconciliation.""" | 82 | """Helper function for performing reconciliation.""" |
504 | 83 | self._reconcile_branch() | 83 | self._reconcile_branches() |
505 | 84 | self._reconcile_repository() | 84 | self._reconcile_repository() |
506 | 85 | 85 | ||
517 | 86 | def _reconcile_branch(self): | 86 | def _reconcile_branches(self): |
518 | 87 | try: | 87 | self.fixed_branch_history = True |
519 | 88 | self.branch = self.bzrdir.open_branch() | 88 | for branch in self.bzrdir.list_branches(): |
520 | 89 | except errors.NotBranchError: | 89 | ui.ui_factory.note('Reconciling branch %s' % branch.base) |
521 | 90 | # Nothing to check here | 90 | branch_reconciler = branch.reconcile(thorough=True) |
522 | 91 | self.fixed_branch_history = None | 91 | self.fixed_branch_history = (self.fixed_branch_history and |
523 | 92 | return | 92 | branch_reconciler.fixed_history) |
514 | 93 | ui.ui_factory.note('Reconciling branch %s' % self.branch.base) | ||
515 | 94 | branch_reconciler = self.branch.reconcile(thorough=True) | ||
516 | 95 | self.fixed_branch_history = branch_reconciler.fixed_history | ||
524 | 96 | 93 | ||
525 | 97 | def _reconcile_repository(self): | 94 | def _reconcile_repository(self): |
526 | 98 | self.repo = self.bzrdir.find_repository() | 95 | self.repo = self.bzrdir.find_repository() |
527 | 99 | 96 | ||
528 | === modified file 'bzrlib/remote.py' | |||
529 | --- bzrlib/remote.py 2010-03-21 21:39:33 +0000 | |||
530 | +++ bzrlib/remote.py 2010-04-17 17:41:17 +0000 | |||
531 | @@ -271,16 +271,19 @@ | |||
532 | 271 | def create_workingtree(self, revision_id=None, from_branch=None): | 271 | def create_workingtree(self, revision_id=None, from_branch=None): |
533 | 272 | raise errors.NotLocalUrl(self.transport.base) | 272 | raise errors.NotLocalUrl(self.transport.base) |
534 | 273 | 273 | ||
536 | 274 | def find_branch_format(self): | 274 | def find_branch_format(self, name=None): |
537 | 275 | """Find the branch 'format' for this bzrdir. | 275 | """Find the branch 'format' for this bzrdir. |
538 | 276 | 276 | ||
539 | 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. |
540 | 278 | """ | 278 | """ |
542 | 279 | b = self.open_branch() | 279 | b = self.open_branch(name=name) |
543 | 280 | return b._format | 280 | return b._format |
544 | 281 | 281 | ||
546 | 282 | def get_branch_reference(self): | 282 | def get_branch_reference(self, name=None): |
547 | 283 | """See BzrDir.get_branch_reference().""" | 283 | """See BzrDir.get_branch_reference().""" |
548 | 284 | if name is not None: | ||
549 | 285 | # XXX JRV20100304: Support opening colocated branches | ||
550 | 286 | raise errors.NoColocatedBranchSupport(self) | ||
551 | 284 | response = self._get_branch_reference() | 287 | response = self._get_branch_reference() |
552 | 285 | if response[0] == 'ref': | 288 | if response[0] == 'ref': |
553 | 286 | return response[1] | 289 | return response[1] |
554 | @@ -317,9 +320,9 @@ | |||
555 | 317 | raise errors.UnexpectedSmartServerResponse(response) | 320 | raise errors.UnexpectedSmartServerResponse(response) |
556 | 318 | return response | 321 | return response |
557 | 319 | 322 | ||
559 | 320 | def _get_tree_branch(self): | 323 | def _get_tree_branch(self, name=None): |
560 | 321 | """See BzrDir._get_tree_branch().""" | 324 | """See BzrDir._get_tree_branch().""" |
562 | 322 | return None, self.open_branch() | 325 | return None, self.open_branch(name=name) |
563 | 323 | 326 | ||
564 | 324 | def open_branch(self, name=None, unsupported=False, | 327 | def open_branch(self, name=None, unsupported=False, |
565 | 325 | ignore_fallbacks=False): | 328 | ignore_fallbacks=False): |
566 | 326 | 329 | ||
567 | === modified file 'bzrlib/switch.py' | |||
568 | --- bzrlib/switch.py 2010-02-17 17:11:16 +0000 | |||
569 | +++ bzrlib/switch.py 2010-04-17 17:41:17 +0000 | |||
570 | @@ -70,7 +70,7 @@ | |||
571 | 70 | branch_format = control.find_branch_format() | 70 | branch_format = control.find_branch_format() |
572 | 71 | if branch_format.get_reference(control) is not None: | 71 | if branch_format.get_reference(control) is not None: |
573 | 72 | # Lightweight checkout: update the branch reference | 72 | # Lightweight checkout: update the branch reference |
575 | 73 | branch_format.set_reference(control, to_branch) | 73 | branch_format.set_reference(control, None, to_branch) |
576 | 74 | else: | 74 | else: |
577 | 75 | b = control.open_branch() | 75 | b = control.open_branch() |
578 | 76 | bound_branch = b.get_bound_location() | 76 | bound_branch = b.get_bound_location() |
579 | 77 | 77 | ||
580 | === modified file 'bzrlib/tests/per_branch/test_branch.py' | |||
581 | --- bzrlib/tests/per_branch/test_branch.py 2010-03-02 22:25:58 +0000 | |||
582 | +++ bzrlib/tests/per_branch/test_branch.py 2010-04-17 17:41:17 +0000 | |||
583 | @@ -668,7 +668,8 @@ | |||
584 | 668 | this_branch = self.make_branch('this') | 668 | this_branch = self.make_branch('this') |
585 | 669 | other_branch = self.make_branch('other') | 669 | other_branch = self.make_branch('other') |
586 | 670 | try: | 670 | try: |
588 | 671 | this_branch._format.set_reference(this_branch.bzrdir, other_branch) | 671 | this_branch._format.set_reference(this_branch.bzrdir, None, |
589 | 672 | other_branch) | ||
590 | 672 | except NotImplementedError: | 673 | except NotImplementedError: |
591 | 673 | # that's ok | 674 | # that's ok |
592 | 674 | pass | 675 | pass |
593 | 675 | 676 | ||
594 | === modified file 'bzrlib/tests/per_bzrdir_colo/test_unsupported.py' | |||
595 | --- bzrlib/tests/per_bzrdir_colo/test_unsupported.py 2010-04-11 19:40:23 +0000 | |||
596 | +++ bzrlib/tests/per_bzrdir_colo/test_unsupported.py 2010-04-17 17:41:17 +0000 | |||
597 | @@ -35,15 +35,7 @@ | |||
598 | 35 | 35 | ||
599 | 36 | class TestNoColocatedSupport(TestCaseWithBzrDir): | 36 | class TestNoColocatedSupport(TestCaseWithBzrDir): |
600 | 37 | 37 | ||
610 | 38 | def test_destroy_colocated_branch(self): | 38 | def make_bzrdir_with_repo(self): |
602 | 39 | branch = self.make_branch('branch') | ||
603 | 40 | # Colocated branches should not be supported *or* | ||
604 | 41 | # destroy_branch should not be supported at all | ||
605 | 42 | self.assertRaises( | ||
606 | 43 | (errors.NoColocatedBranchSupport, errors.UnsupportedOperation), | ||
607 | 44 | branch.bzrdir.destroy_branch, 'colo') | ||
608 | 45 | |||
609 | 46 | def test_create_colo_branch(self): | ||
611 | 47 | # a bzrdir can construct a branch and repository for itself. | 39 | # a bzrdir can construct a branch and repository for itself. |
612 | 48 | if not self.bzrdir_format.is_supported(): | 40 | if not self.bzrdir_format.is_supported(): |
613 | 49 | # unsupported formats are not loopback testable | 41 | # unsupported formats are not loopback testable |
614 | @@ -53,7 +45,27 @@ | |||
615 | 53 | t = get_transport(self.get_url()) | 45 | t = get_transport(self.get_url()) |
616 | 54 | made_control = self.bzrdir_format.initialize(t.base) | 46 | made_control = self.bzrdir_format.initialize(t.base) |
617 | 55 | made_repo = made_control.create_repository() | 47 | made_repo = made_control.create_repository() |
618 | 48 | return made_control | ||
619 | 49 | |||
620 | 50 | def test_destroy_colocated_branch(self): | ||
621 | 51 | branch = self.make_branch('branch') | ||
622 | 52 | # Colocated branches should not be supported *or* | ||
623 | 53 | # destroy_branch should not be supported at all | ||
624 | 54 | self.assertRaises( | ||
625 | 55 | (errors.NoColocatedBranchSupport, errors.UnsupportedOperation), | ||
626 | 56 | branch.bzrdir.destroy_branch, 'colo') | ||
627 | 57 | |||
628 | 58 | def test_create_colo_branch(self): | ||
629 | 59 | made_control = self.make_bzrdir_with_repo() | ||
630 | 56 | self.assertRaises(errors.NoColocatedBranchSupport, | 60 | self.assertRaises(errors.NoColocatedBranchSupport, |
631 | 57 | made_control.create_branch, "colo") | 61 | made_control.create_branch, "colo") |
632 | 58 | 62 | ||
633 | 63 | def test_branch_transport(self): | ||
634 | 64 | made_control = self.make_bzrdir_with_repo() | ||
635 | 65 | self.assertRaises(errors.NoColocatedBranchSupport, | ||
636 | 66 | made_control.get_branch_transport, None, "colo") | ||
637 | 59 | 67 | ||
638 | 68 | def test_get_branch_reference(self): | ||
639 | 69 | made_control = self.make_bzrdir_with_repo() | ||
640 | 70 | self.assertRaises(errors.NoColocatedBranchSupport, | ||
641 | 71 | made_control.get_branch_reference, "colo") | ||
642 | 60 | 72 | ||
643 | === modified file 'bzrlib/tests/test_branch.py' | |||
644 | --- bzrlib/tests/test_branch.py 2010-03-11 13:14:37 +0000 | |||
645 | +++ bzrlib/tests/test_branch.py 2010-04-17 17:41:17 +0000 | |||
646 | @@ -577,3 +577,40 @@ | |||
647 | 577 | self.assertEqual(['lock_write', 'func called', 'unlock'], self._calls) | 577 | self.assertEqual(['lock_write', 'func called', 'unlock'], self._calls) |
648 | 578 | 578 | ||
649 | 579 | 579 | ||
650 | 580 | class TestExtractColocatedName(tests.TestCase): | ||
651 | 581 | |||
652 | 582 | def test_no_colocated(self): | ||
653 | 583 | self.assertEquals(("/some/path", None), | ||
654 | 584 | _mod_branch.split_colocated_name("/some/path")) | ||
655 | 585 | |||
656 | 586 | def test_colocated(self): | ||
657 | 587 | self.assertEquals(("/some/path", "tip"), | ||
658 | 588 | _mod_branch.split_colocated_name("/some/path,tip")) | ||
659 | 589 | |||
660 | 590 | def test_colocated_uses_right_comma(self): | ||
661 | 591 | self.assertEquals(("/some,dir/path", "tip"), | ||
662 | 592 | _mod_branch.split_colocated_name("/some,dir/path,tip")) | ||
663 | 593 | |||
664 | 594 | def test_colocated_allows_slashes(self): | ||
665 | 595 | self.assertEquals(("/somedir/path", "heads%2Ftip"), | ||
666 | 596 | _mod_branch.split_colocated_name("/somedir/path,heads%2Ftip")) | ||
667 | 597 | |||
668 | 598 | def test_none(self): | ||
669 | 599 | self.assertEquals((None, None), | ||
670 | 600 | _mod_branch.split_colocated_name(None)) | ||
671 | 601 | |||
672 | 602 | |||
673 | 603 | class TestJoinColocatedName(tests.TestCase): | ||
674 | 604 | |||
675 | 605 | def test_default(self): | ||
676 | 606 | self.assertEquals("/somedir/path", | ||
677 | 607 | _mod_branch.join_colocated_name("/somedir/path", None)) | ||
678 | 608 | |||
679 | 609 | def test_colocated(self): | ||
680 | 610 | self.assertEquals("/somedir/path,brrr", | ||
681 | 611 | _mod_branch.join_colocated_name("/somedir/path", "brrr")) | ||
682 | 612 | |||
683 | 613 | def test_invalid_branch_name(self): | ||
684 | 614 | self.assertRaises(errors.InvalidColocatedBranchName, | ||
685 | 615 | _mod_branch.join_colocated_name, "/somedir/path", "brr,brr,brr") | ||
686 | 616 | |||
687 | 580 | 617 | ||
688 | === modified file 'bzrlib/tests/test_remote.py' | |||
689 | --- bzrlib/tests/test_remote.py 2010-02-23 07:43:11 +0000 | |||
690 | +++ bzrlib/tests/test_remote.py 2010-04-17 17:41:17 +0000 | |||
691 | @@ -584,7 +584,7 @@ | |||
692 | 584 | # _get_tree_branch is a form of open_branch, but it should only ask for | 584 | # _get_tree_branch is a form of open_branch, but it should only ask for |
693 | 585 | # branch opening, not any other network requests. | 585 | # branch opening, not any other network requests. |
694 | 586 | calls = [] | 586 | calls = [] |
696 | 587 | def open_branch(): | 587 | def open_branch(name=None): |
697 | 588 | calls.append("Called") | 588 | calls.append("Called") |
698 | 589 | return "a-branch" | 589 | return "a-branch" |
699 | 590 | transport = MemoryTransport() | 590 | transport = MemoryTransport() |
700 | 591 | 591 | ||
701 | === modified file 'bzrlib/tests/test_urlutils.py' | |||
702 | --- bzrlib/tests/test_urlutils.py 2010-02-17 17:11:16 +0000 | |||
703 | +++ bzrlib/tests/test_urlutils.py 2010-04-17 17:41:17 +0000 | |||
704 | @@ -270,6 +270,17 @@ | |||
705 | 270 | self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '..') | 270 | self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '..') |
706 | 271 | self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '/..') | 271 | self.assertRaises(InvalidURLJoin, urlutils.joinpath, '/', '/..') |
707 | 272 | 272 | ||
708 | 273 | def test_join_subsegments(self): | ||
709 | 274 | join_subsegments = urlutils.join_subsegments | ||
710 | 275 | self.assertEquals("/somedir/path", | ||
711 | 276 | join_subsegments("/somedir/path")) | ||
712 | 277 | self.assertEquals("/somedir/path,brrr", | ||
713 | 278 | join_subsegments("/somedir/path", "brrr")) | ||
714 | 279 | self.assertRaises(InvalidURLJoin, | ||
715 | 280 | join_subsegments, "/somedir/path", "brr,brr,brr") | ||
716 | 281 | self.assertEquals("/somedir/path,bla,bar", | ||
717 | 282 | join_subsegments("/somedir/path", "bla", "bar")) | ||
718 | 283 | |||
719 | 273 | def test_function_type(self): | 284 | def test_function_type(self): |
720 | 274 | if sys.platform == 'win32': | 285 | if sys.platform == 'win32': |
721 | 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) |
722 | @@ -412,6 +423,19 @@ | |||
723 | 412 | self.assertEqual(('path/..', 'foo'), split('path/../foo')) | 423 | self.assertEqual(('path/..', 'foo'), split('path/../foo')) |
724 | 413 | self.assertEqual(('../path', 'foo'), split('../path/foo')) | 424 | self.assertEqual(('../path', 'foo'), split('../path/foo')) |
725 | 414 | 425 | ||
726 | 426 | def test_split_subsegments(self): | ||
727 | 427 | split_subsegments = urlutils.split_subsegments | ||
728 | 428 | self.assertEquals(("/some/path", []), | ||
729 | 429 | split_subsegments("/some/path")) | ||
730 | 430 | self.assertEquals(("/some/path", ["tip"]), | ||
731 | 431 | split_subsegments("/some/path,tip")) | ||
732 | 432 | self.assertEquals(("/some,dir/path", ["tip"]), | ||
733 | 433 | split_subsegments("/some,dir/path,tip")) | ||
734 | 434 | self.assertEquals(("/somedir/path", ["heads%2Ftip"]), | ||
735 | 435 | split_subsegments("/somedir/path,heads%2Ftip")) | ||
736 | 436 | self.assertEquals(("/somedir/path", ["heads%2Ftip", "bar"]), | ||
737 | 437 | split_subsegments("/somedir/path,heads%2Ftip,bar")) | ||
738 | 438 | |||
739 | 415 | def test_win32_strip_local_trailing_slash(self): | 439 | def test_win32_strip_local_trailing_slash(self): |
740 | 416 | strip = urlutils._win32_strip_local_trailing_slash | 440 | strip = urlutils._win32_strip_local_trailing_slash |
741 | 417 | self.assertEqual('file://', strip('file://')) | 441 | self.assertEqual('file://', strip('file://')) |
742 | 418 | 442 | ||
743 | === modified file 'bzrlib/urlutils.py' | |||
744 | --- bzrlib/urlutils.py 2010-02-17 17:11:16 +0000 | |||
745 | +++ bzrlib/urlutils.py 2010-04-17 17:41:17 +0000 | |||
746 | @@ -469,6 +469,32 @@ | |||
747 | 469 | return url_base + head, tail | 469 | return url_base + head, tail |
748 | 470 | 470 | ||
749 | 471 | 471 | ||
750 | 472 | def split_subsegments(url): | ||
751 | 473 | """Split the subsegment of the last segment of a URL. | ||
752 | 474 | |||
753 | 475 | :param url: A relative or absolute URL | ||
754 | 476 | :return: (url, subsegments) | ||
755 | 477 | """ | ||
756 | 478 | (parent_url, child_dir) = split(url) | ||
757 | 479 | subsegments = child_dir.split(",") | ||
758 | 480 | if len(subsegments) == 1: | ||
759 | 481 | return (url, []) | ||
760 | 482 | return (join(parent_url, subsegments[0]), subsegments[1:]) | ||
761 | 483 | |||
762 | 484 | |||
763 | 485 | def join_subsegments(base, *subsegments): | ||
764 | 486 | """Create a new URL by adding subsegments to an existing one. | ||
765 | 487 | |||
766 | 488 | """ | ||
767 | 489 | if not subsegments: | ||
768 | 490 | return base | ||
769 | 491 | for subsegment in subsegments: | ||
770 | 492 | if "," in subsegment: | ||
771 | 493 | raise errors.InvalidURLJoin(", exists in subsegments", | ||
772 | 494 | base, subsegments) | ||
773 | 495 | return ",".join((base,) + subsegments) | ||
774 | 496 | |||
775 | 497 | |||
776 | 472 | def _win32_strip_local_trailing_slash(url): | 498 | def _win32_strip_local_trailing_slash(url): |
777 | 473 | """Strip slashes after the drive letter""" | 499 | """Strip slashes after the drive letter""" |
778 | 474 | if len(url) > WIN32_MIN_ABS_FILEURL_LENGTH: | 500 | if len(url) > WIN32_MIN_ABS_FILEURL_LENGTH: |
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.