Merge lp:bzr-loom into lp:~launchpad-pqm/bzr-loom/trunk
- trunk
- Merge into trunk
Proposed by
Aaron Bentley
Status: | Merged |
---|---|
Approved by: | Paul Hummer |
Approved revision: | 126 |
Merged at revision: | 48 |
Proposed branch: | lp:bzr-loom |
Merge into: | lp:~launchpad-pqm/bzr-loom/trunk |
Diff against target: |
2313 lines (+1033/-321) 18 files modified
.testr.conf (+4/-0) HOWTO (+19/-1) NEWS (+102/-2) README (+5/-3) TODO (+1/-2) __init__.py (+26/-7) branch.py (+226/-126) commands.py (+107/-40) formats.py (+74/-0) revspec.py (+67/-32) setup.py (+3/-5) tests/__init__.py (+4/-1) tests/blackbox.py (+147/-19) tests/test_branch.py (+84/-35) tests/test_revspec.py (+49/-5) tests/test_tree.py (+29/-13) tree.py (+58/-30) version.py (+28/-0) |
To merge this branch: | bzr merge lp:bzr-loom |
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paul Hummer (community) | Approve | ||
Review via email: mp+31873@code.launchpad.net |
Commit message
Update to tip for bzr 2.2 support.
Description of the change
Update Launchpad copy of bzr-loom to trunk tip for bzr 2.2 support.
(Otherwise, you get API breakage.)
To post a comment you must log in.
Revision history for this message
Paul Hummer (rockstar) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === added file '.testr.conf' | |||
2 | --- .testr.conf 1970-01-01 00:00:00 +0000 | |||
3 | +++ .testr.conf 2010-08-05 18:57:44 +0000 | |||
4 | @@ -0,0 +1,4 @@ | |||
5 | 1 | [DEFAULT] | ||
6 | 2 | # not quite idea, because 'all tests' is too many, and 'just loom' is too few. | ||
7 | 3 | test_command=bzr selftest --subunit $IDOPTION | ||
8 | 4 | test_id_option=--load-list $IDFILE | ||
9 | 0 | 5 | ||
10 | === modified file 'HOWTO' | |||
11 | --- HOWTO 2008-09-12 01:55:17 +0000 | |||
12 | +++ HOWTO 2010-08-05 18:57:44 +0000 | |||
13 | @@ -47,6 +47,11 @@ | |||
14 | 47 | 47 | ||
15 | 48 | This will convert your branch to a loom - at this point you will require the | 48 | This will convert your branch to a loom - at this point you will require the |
16 | 49 | loom plugin to use bzr on it. It will also create a thread called 'upstream'. | 49 | loom plugin to use bzr on it. It will also create a thread called 'upstream'. |
17 | 50 | |||
18 | 51 | The bzr ``nick`` is used to index into the threads in the loom. The current | ||
19 | 52 | thread is the bzr nick for the branch and is recorded in commits as such. You | ||
20 | 53 | can use ``bzr nick newname`` to change the name of a thread. | ||
21 | 54 | |||
22 | 50 | If you do 'bzr push' to a new branch now, it will make the remote branch be a | 55 | If you do 'bzr push' to a new branch now, it will make the remote branch be a |
23 | 51 | loom as well. If you push to an existing normal bzr branch, then the current | 56 | loom as well. If you push to an existing normal bzr branch, then the current |
24 | 52 | thread of your loom is what will be pushed. You can use this to publish | 57 | thread of your loom is what will be pushed. You can use this to publish |
25 | @@ -177,7 +182,7 @@ | |||
26 | 177 | When you are doing an update to upstream and they have merged a patch, your | 182 | When you are doing an update to upstream and they have merged a patch, your |
27 | 178 | thread will suddenly lose all its changes. Lets say in the example above that | 183 | thread will suddenly lose all its changes. Lets say in the example above that |
28 | 179 | upstream have merged the autoconf update. When you are updating that thread, | 184 | upstream have merged the autoconf update. When you are updating that thread, |
30 | 180 | add in a call to ``diff -r thread:`` and you will see no changes. | 185 | add in a call to ``diff -r below:`` and you will see no changes. |
31 | 181 | 186 | ||
32 | 182 | % bzr up-thread | 187 | % bzr up-thread |
33 | 183 | All changes applied successfully. | 188 | All changes applied successfully. |
34 | @@ -201,3 +206,16 @@ | |||
35 | 201 | debian | 206 | debian |
36 | 202 | =>fix 64-bit compilation | 207 | =>fix 64-bit compilation |
37 | 203 | upstream | 208 | upstream |
38 | 209 | |||
39 | 210 | |||
40 | 211 | Showing a single patch | ||
41 | 212 | ---------------------- | ||
42 | 213 | |||
43 | 214 | You can show a single patch without knowing the names of other threads by using | ||
44 | 215 | the below: and thread: revision specifiers:: | ||
45 | 216 | |||
46 | 217 | % bzr show-loom | ||
47 | 218 | debian | ||
48 | 219 | =>update-configure | ||
49 | 220 | upstream | ||
50 | 221 | % bzr diff -r below:update-configure..thread:update-configure | ||
51 | 204 | 222 | ||
52 | === modified file 'NEWS' | |||
53 | --- NEWS 2008-11-19 07:18:17 +0000 | |||
54 | +++ NEWS 2010-08-05 18:57:44 +0000 | |||
55 | @@ -5,7 +5,95 @@ | |||
56 | 5 | .. contents:: | 5 | .. contents:: |
57 | 6 | 6 | ||
58 | 7 | IN DEVELOPMENT | 7 | IN DEVELOPMENT |
60 | 8 | -------------- | 8 | ============== |
61 | 9 | |||
62 | 10 | NOTES WHEN UPGRADING | ||
63 | 11 | -------------------- | ||
64 | 12 | |||
65 | 13 | * bzr-loom requires bzr 2.2.0 (or very recent 2.2b releases) due to an API | ||
66 | 14 | change in bzr needed to fix branching and pulling of looms. On older versions | ||
67 | 15 | of bzr bzr-loom will still work for most operations but will fail when making | ||
68 | 16 | new branches as part of a push or branch operation. (Robert Collins, #201613) | ||
69 | 17 | |||
70 | 18 | CHANGES | ||
71 | 19 | ------- | ||
72 | 20 | |||
73 | 21 | * --auto is now the default on up-thread. You can supply a thread name to stop | ||
74 | 22 | at a given thread, or --manual to go up a single thread. (Aaron Bentley) | ||
75 | 23 | |||
76 | 24 | * ``bzr combine-thread`` now accepts a ``--force`` option. | ||
77 | 25 | |||
78 | 26 | FEATURES | ||
79 | 27 | -------- | ||
80 | 28 | |||
81 | 29 | * A new revision specifier ``below:`` has been added. (Robert Collins, #195282) | ||
82 | 30 | |||
83 | 31 | IMPROVEMENTS | ||
84 | 32 | ------------ | ||
85 | 33 | |||
86 | 34 | * Loom now takes advantage of lazy loading of bzr objects (though not to a | ||
87 | 35 | complete degree), reducing the overhead of having it installed. | ||
88 | 36 | (Robert Collins) | ||
89 | 37 | |||
90 | 38 | BUGFIXES | ||
91 | 39 | -------- | ||
92 | 40 | |||
93 | 41 | * ``bzr combine-thread`` will no longer combine threads without ``--force`` | ||
94 | 42 | when the thread being removed has work not merged into either the thread | ||
95 | 43 | above or below. (Robert Collins, #506235) | ||
96 | 44 | |||
97 | 45 | * ``bzr loomify`` explicitly checks that branches being converted are not Looms | ||
98 | 46 | already. This should not have been needed, but apparently it was. | ||
99 | 47 | (Robert Collins, #600452) | ||
100 | 48 | |||
101 | 49 | * ``bzr nick`` will now rename a thread rather than setting the current thread | ||
102 | 50 | pointer to an invalid value. (Robert Collins, #203203, #260947, #304608) | ||
103 | 51 | |||
104 | 52 | * ``bzr nick`` will now rename the branch too. (Vincent Ladeuil, #606174) | ||
105 | 53 | |||
106 | 54 | * ``switch`` now accepts the ``--directory`` option. (Vincent Ladeuil, #595563) | ||
107 | 55 | |||
108 | 56 | * The ``thread:`` revision specifier will no longer throw an attribute error | ||
109 | 57 | when used on a normal branch. (Robert Collins, #231283) | ||
110 | 58 | |||
111 | 59 | API BREAKS | ||
112 | 60 | ---------- | ||
113 | 61 | |||
114 | 62 | TESTING | ||
115 | 63 | ------- | ||
116 | 64 | |||
117 | 65 | INTERNALS | ||
118 | 66 | --------- | ||
119 | 67 | |||
120 | 68 | 2.1 | ||
121 | 69 | === | ||
122 | 70 | |||
123 | 71 | NOTES WHEN UPGRADING: | ||
124 | 72 | |||
125 | 73 | CHANGES: | ||
126 | 74 | |||
127 | 75 | FEATURES: | ||
128 | 76 | |||
129 | 77 | IMPROVEMENTS: | ||
130 | 78 | |||
131 | 79 | BUGFIXES: | ||
132 | 80 | |||
133 | 81 | * Stop using APIs deprecated for 2.1.0 (child progress bars for | ||
134 | 82 | merge and trace.info). (Vincent Ladeuil, #528472) | ||
135 | 83 | |||
136 | 84 | * Work with changes to bzr trunk - colocated branches and switch -r. | ||
137 | 85 | |||
138 | 86 | API BREAKS: | ||
139 | 87 | |||
140 | 88 | TESTING: | ||
141 | 89 | |||
142 | 90 | INTERNALS: | ||
143 | 91 | |||
144 | 92 | * .testr.conf added to help use with testr - still need to specify what tests | ||
145 | 93 | to run. (Robert Collins) | ||
146 | 94 | |||
147 | 95 | 2.0 | ||
148 | 96 | === | ||
149 | 9 | 97 | ||
150 | 10 | NOTES WHEN UPGRADING: | 98 | NOTES WHEN UPGRADING: |
151 | 11 | 99 | ||
152 | @@ -21,14 +109,26 @@ | |||
153 | 21 | * ``bzr switch`` now accepts ``top:`` and ``bottom:`` to jump to the top | 109 | * ``bzr switch`` now accepts ``top:`` and ``bottom:`` to jump to the top |
154 | 22 | and bottom thread respectively. (Jonathan Lange) | 110 | and bottom thread respectively. (Jonathan Lange) |
155 | 23 | 111 | ||
156 | 112 | * ``bzr switch -b newthread`` now works. (Robert Collins, #433811) | ||
157 | 113 | |||
158 | 24 | * ``bzr push`` now pushes the last-loom rather than creating an empty loom. | 114 | * ``bzr push`` now pushes the last-loom rather than creating an empty loom. |
159 | 25 | (Robert Collins, #201613) | 115 | (Robert Collins, #201613) |
160 | 26 | 116 | ||
161 | 27 | * ``up`` and ``down`` are now aliases for ``up-thread`` and | 117 | * ``up`` and ``down`` are now aliases for ``up-thread`` and |
162 | 28 | ``down-thread`` respectively. | 118 | ``down-thread`` respectively. |
163 | 29 | 119 | ||
164 | 120 | * ``up-thread`` now notifies when a thread becomes empty. This is a step | ||
165 | 121 | towards removing it automatically/prompting to do so. | ||
166 | 122 | (James Westby, #195133) | ||
167 | 123 | |||
168 | 30 | BUGFIXES: | 124 | BUGFIXES: |
169 | 31 | 125 | ||
170 | 126 | * ``pull`` expects the keywork local. (Mark Lee, #379347) | ||
171 | 127 | |||
172 | 128 | * ``setup.py`` doesn't actually install. (Mark Lee, #379069) | ||
173 | 129 | |||
174 | 130 | * module has no attribute ``PushResult``. (Robert Collins) | ||
175 | 131 | |||
176 | 32 | API BREAKS: | 132 | API BREAKS: |
177 | 33 | 133 | ||
178 | 34 | TESTING: | 134 | TESTING: |
179 | @@ -37,7 +137,7 @@ | |||
180 | 37 | 137 | ||
181 | 38 | 138 | ||
182 | 39 | 1.3 | 139 | 1.3 |
184 | 40 | --- | 140 | === |
185 | 41 | 141 | ||
186 | 42 | IMPROVEMENTS: | 142 | IMPROVEMENTS: |
187 | 43 | 143 | ||
188 | 44 | 144 | ||
189 | === modified file 'README' | |||
190 | --- README 2008-09-12 01:55:17 +0000 | |||
191 | +++ README 2010-08-05 18:57:44 +0000 | |||
192 | @@ -77,9 +77,11 @@ | |||
193 | 77 | branch will pull into the current thread of a loom. This assymetry makes it easy | 77 | branch will pull into the current thread of a loom. This assymetry makes it easy |
194 | 78 | to work with developers who are not using looms. | 78 | to work with developers who are not using looms. |
195 | 79 | 79 | ||
199 | 80 | Loom also adds a new revision specifier 'thread:'. You can use this to diff | 80 | Loom also adds new revision specifiers 'thread:' and 'below:'. You can use these |
200 | 81 | against threads in the current loom. For example: 'bzr diff -r thread:' will | 81 | to diff against threads in the current Loom. For instance, 'bzr diff -r |
201 | 82 | show you the difference between the current thread and the thread below it. | 82 | thread:' will show you the different between the thread below yours, and your |
202 | 83 | thread. See ``bzr help revisionspec`` for the detailed help on these two | ||
203 | 84 | revision specifiers. | ||
204 | 83 | 85 | ||
205 | 84 | 86 | ||
206 | 85 | Documentation | 87 | Documentation |
207 | 86 | 88 | ||
208 | === modified file 'TODO' | |||
209 | --- TODO 2008-01-20 07:34:29 +0000 | |||
210 | +++ TODO 2010-08-05 18:57:44 +0000 | |||
211 | @@ -95,7 +95,7 @@ | |||
212 | 95 | TODO either means changing 'diff's defaults, adding a flag to diff, or using | 95 | TODO either means changing 'diff's defaults, adding a flag to diff, or using |
213 | 96 | a different revspec prefix; or something like that.) | 96 | a different revspec prefix; or something like that.) |
214 | 97 | - export-patch to export the diff from this thread to the lower thread (using -ancestry logic) to a file named as the warp is named. (what about / ?) | 97 | - export-patch to export the diff from this thread to the lower thread (using -ancestry logic) to a file named as the warp is named. (what about / ?) |
216 | 98 | - during up-thread, if we could pull or if there is no diff, then the thread has been merged, offer to remove it. | 98 | - during up-thread, if we could pull or if there is no diff, then the thread has been merged, offer to remove it. (Currently suggests to remove it). |
217 | 99 | - loom to have the same 'tree root id' as its branches, to allow nested looms by reference. EEK!. | 99 | - loom to have the same 'tree root id' as its branches, to allow nested looms by reference. EEK!. |
218 | 100 | - show-loom to allow -r -1. | 100 | - show-loom to allow -r -1. |
219 | 101 | - combine-thread to warn if the thread being combined has changes not present in the one below it. I.e. by ancestry, or by doing a merge and recording differences. For bonus points, do the merge, but record the lower thread as the last-revision in the tree still, and set no pending-merges. This preserves the difference whilst still combining the threads. | 101 | - combine-thread to warn if the thread being combined has changes not present in the one below it. I.e. by ancestry, or by doing a merge and recording differences. For bonus points, do the merge, but record the lower thread as the last-revision in the tree still, and set no pending-merges. This preserves the difference whilst still combining the threads. |
220 | @@ -110,5 +110,4 @@ | |||
221 | 110 | therein as well, to support uncommit between record calls. | 110 | therein as well, to support uncommit between record calls. |
222 | 111 | - combine-thread should de-dup penmding merges (use case: up-thread finds a fully merged thread so there are pending merges but no diff between threads; this is when combine-thread is often called). | 111 | - combine-thread should de-dup penmding merges (use case: up-thread finds a fully merged thread so there are pending merges but no diff between threads; this is when combine-thread is often called). |
223 | 112 | - support tags on push/pull in looms | 112 | - support tags on push/pull in looms |
224 | 113 | - bzr nick <name> should rename the current thread (by adding one with the new name and in current-thread deleted the active location - same as merging a delete. | ||
225 | 114 | - perhaps bzr send should send the whole loom ? (e.g. as a patch bomb - a series of patches?) | 113 | - perhaps bzr send should send the whole loom ? (e.g. as a patch bomb - a series of patches?) |
226 | 115 | 114 | ||
227 | === modified file '__init__.py' | |||
228 | --- __init__.py 2008-09-12 01:55:17 +0000 | |||
229 | +++ __init__.py 2010-08-05 18:57:44 +0000 | |||
230 | @@ -48,19 +48,21 @@ | |||
231 | 48 | to remove a thread which has been merged into upstream. | 48 | to remove a thread which has been merged into upstream. |
232 | 49 | 49 | ||
233 | 50 | 50 | ||
237 | 51 | Loom also adds a new revision specifier 'thread:'. You can use this to diff | 51 | Loom also adds new revision specifiers 'thread:' and 'below:'. You can use these |
238 | 52 | against threads in the current Loom. For instance, 'bzr diff -r thread:' will | 52 | to diff against threads in the current Loom. For instance, 'bzr diff -r |
239 | 53 | show you the different between the thread below yours, and your thread. | 53 | thread:' will show you the different between the thread below yours, and your |
240 | 54 | thread. See ``bzr help revisionspec`` for the detailed help on these two | ||
241 | 55 | revision specifiers. | ||
242 | 54 | """ | 56 | """ |
243 | 55 | 57 | ||
245 | 56 | version_info = (1, 4, 0, 'dev', 0) | 58 | from version import bzr_plugin_version as version_info |
246 | 57 | 59 | ||
247 | 58 | import bzrlib.builtins | 60 | import bzrlib.builtins |
248 | 59 | import bzrlib.commands | 61 | import bzrlib.commands |
249 | 62 | import bzrlib.revisionspec | ||
250 | 60 | 63 | ||
251 | 61 | import branch | ||
252 | 62 | import commands | 64 | import commands |
254 | 63 | import revspec | 65 | import formats |
255 | 64 | 66 | ||
256 | 65 | 67 | ||
257 | 66 | for command in [ | 68 | for command in [ |
258 | @@ -74,8 +76,12 @@ | |||
259 | 74 | 'show_loom', | 76 | 'show_loom', |
260 | 75 | 'up_thread', | 77 | 'up_thread', |
261 | 76 | ]: | 78 | ]: |
263 | 77 | bzrlib.commands.register_command(getattr(commands, 'cmd_' + command)) | 79 | bzrlib.commands.plugin_cmds.register_lazy('cmd_' + command, [], |
264 | 80 | 'bzrlib.plugins.loom.commands') | ||
265 | 78 | 81 | ||
266 | 82 | # XXX: bzr fix needed: for status and switch, we have to register directly, not | ||
267 | 83 | # lazily, because register_lazy does not stack in the same way register_command | ||
268 | 84 | # does. | ||
269 | 79 | if not hasattr(bzrlib.builtins, "cmd_switch"): | 85 | if not hasattr(bzrlib.builtins, "cmd_switch"): |
270 | 80 | # provide a switch command (allows | 86 | # provide a switch command (allows |
271 | 81 | bzrlib.commands.register_command(getattr(commands, 'cmd_switch')) | 87 | bzrlib.commands.register_command(getattr(commands, 'cmd_switch')) |
272 | @@ -86,6 +92,19 @@ | |||
273 | 86 | commands.cmd_status._original_command = bzrlib.commands.register_command( | 92 | commands.cmd_status._original_command = bzrlib.commands.register_command( |
274 | 87 | commands.cmd_status, True) | 93 | commands.cmd_status, True) |
275 | 88 | 94 | ||
276 | 95 | revspec_registry = getattr(bzrlib.revisionspec, 'revspec_registry', None) | ||
277 | 96 | if revspec_registry is not None: | ||
278 | 97 | revspec_registry.register_lazy('thread:', 'bzrlib.plugins.loom.revspec', | ||
279 | 98 | 'RevisionSpecThread') | ||
280 | 99 | revspec_registry.register_lazy('below:', 'bzrlib.plugins.loom.revspec', | ||
281 | 100 | 'RevisionSpecBelow') | ||
282 | 101 | else: | ||
283 | 102 | import revspec | ||
284 | 103 | bzrlib.revisionspec.SPEC_TYPES.append(revspec.RevisionSpecThread) | ||
285 | 104 | bzrlib.revisionspec.SPEC_TYPES.append(revspec.RevisionSpecBelow) | ||
286 | 105 | |||
287 | 106 | #register loom formats | ||
288 | 107 | formats.register_formats() | ||
289 | 89 | 108 | ||
290 | 90 | def test_suite(): | 109 | def test_suite(): |
291 | 91 | import bzrlib.plugins.loom.tests | 110 | import bzrlib.plugins.loom.tests |
292 | 92 | 111 | ||
293 | === modified file 'branch.py' | |||
294 | --- branch.py 2009-04-23 09:09:12 +0000 | |||
295 | +++ branch.py 2010-08-05 18:57:44 +0000 | |||
296 | @@ -31,13 +31,14 @@ | |||
297 | 31 | from bzrlib.decorators import needs_read_lock, needs_write_lock | 31 | from bzrlib.decorators import needs_read_lock, needs_write_lock |
298 | 32 | import bzrlib.errors | 32 | import bzrlib.errors |
299 | 33 | import bzrlib.osutils | 33 | import bzrlib.osutils |
301 | 34 | from bzrlib import symbol_versioning | 34 | from bzrlib import remote, symbol_versioning |
302 | 35 | import bzrlib.trace | 35 | import bzrlib.trace |
303 | 36 | import bzrlib.ui | 36 | import bzrlib.ui |
304 | 37 | from bzrlib.revision import is_null, NULL_REVISION | 37 | from bzrlib.revision import is_null, NULL_REVISION |
305 | 38 | import bzrlib.tree | 38 | import bzrlib.tree |
306 | 39 | import bzrlib.urlutils | 39 | import bzrlib.urlutils |
307 | 40 | 40 | ||
308 | 41 | import formats | ||
309 | 41 | import loom_io | 42 | import loom_io |
310 | 42 | import loom_state | 43 | import loom_state |
311 | 43 | 44 | ||
312 | @@ -45,6 +46,26 @@ | |||
313 | 45 | EMPTY_REVISION = 'empty:' | 46 | EMPTY_REVISION = 'empty:' |
314 | 46 | 47 | ||
315 | 47 | 48 | ||
316 | 49 | def create_thread(loom, thread_name): | ||
317 | 50 | """Create a thread in the branch loom called thread.""" | ||
318 | 51 | require_loom_branch(loom) | ||
319 | 52 | loom.lock_write() | ||
320 | 53 | try: | ||
321 | 54 | loom.new_thread(thread_name, loom.nick) | ||
322 | 55 | loom._set_nick(thread_name) | ||
323 | 56 | finally: | ||
324 | 57 | loom.unlock() | ||
325 | 58 | |||
326 | 59 | |||
327 | 60 | class AlreadyLoom(bzrlib.errors.BzrError): | ||
328 | 61 | |||
329 | 62 | _fmt = """Loom %(loom)s is already a loom.""" | ||
330 | 63 | |||
331 | 64 | def __init__(self, loom): | ||
332 | 65 | bzrlib.errors.BzrError.__init__(self) | ||
333 | 66 | self.loom = loom | ||
334 | 67 | |||
335 | 68 | |||
336 | 48 | def loomify(branch): | 69 | def loomify(branch): |
337 | 49 | """Convert branch to a loom. | 70 | """Convert branch to a loom. |
338 | 50 | 71 | ||
339 | @@ -53,6 +74,12 @@ | |||
340 | 53 | try: | 74 | try: |
341 | 54 | branch.lock_write() | 75 | branch.lock_write() |
342 | 55 | try: | 76 | try: |
343 | 77 | require_loom_branch(branch) | ||
344 | 78 | except NotALoom: | ||
345 | 79 | pass | ||
346 | 80 | else: | ||
347 | 81 | raise AlreadyLoom(branch) | ||
348 | 82 | try: | ||
349 | 56 | format = { | 83 | format = { |
350 | 57 | bzrlib.branch.BzrBranchFormat5: BzrBranchLoomFormat1, | 84 | bzrlib.branch.BzrBranchFormat5: BzrBranchLoomFormat1, |
351 | 58 | bzrlib.branch.BzrBranchFormat6: BzrBranchLoomFormat6, | 85 | bzrlib.branch.BzrBranchFormat6: BzrBranchLoomFormat6, |
352 | @@ -65,25 +92,12 @@ | |||
353 | 65 | branch.unlock() | 92 | branch.unlock() |
354 | 66 | 93 | ||
355 | 67 | 94 | ||
370 | 68 | def require_loom_branch(branch): | 95 | require_loom_branch = formats.require_loom_branch |
371 | 69 | """Return None if branch is already loomified, or raise NotALoom.""" | 96 | NotALoom = formats.NotALoom |
358 | 70 | if not branch._format.__class__ in LOOM_FORMATS: | ||
359 | 71 | raise NotALoom(branch) | ||
360 | 72 | |||
361 | 73 | |||
362 | 74 | class NotALoom(bzrlib.errors.BzrError): | ||
363 | 75 | |||
364 | 76 | _fmt = ("The branch %(branch)s is not a loom. " | ||
365 | 77 | "You can use 'bzr loomify' to make it into a loom.") | ||
366 | 78 | |||
367 | 79 | def __init__(self, branch): | ||
368 | 80 | bzrlib.errors.BzrError.__init__(self) | ||
369 | 81 | self.branch = branch | ||
372 | 82 | 97 | ||
373 | 83 | 98 | ||
374 | 84 | class LoomThreadError(bzrlib.errors.BzrError): | 99 | class LoomThreadError(bzrlib.errors.BzrError): |
377 | 85 | 100 | """Base class for Loom-Thread errors.""" | |
376 | 86 | _fmt = """Base class for Loom-Thread errors.""" | ||
378 | 87 | 101 | ||
379 | 88 | def __init__(self, branch, thread): | 102 | def __init__(self, branch, thread): |
380 | 89 | bzrlib.errors.BzrError.__init__(self) | 103 | bzrlib.errors.BzrError.__init__(self) |
381 | @@ -198,14 +212,14 @@ | |||
382 | 198 | if len(threads) <= current_index: | 212 | if len(threads) <= current_index: |
383 | 199 | # removed the end | 213 | # removed the end |
384 | 200 | # take the new end thread | 214 | # take the new end thread |
386 | 201 | self.nick = threads[-1][0] | 215 | self._set_nick(threads[-1][0]) |
387 | 202 | new_rev = threads[-1][1] | 216 | new_rev = threads[-1][1] |
388 | 203 | if new_rev == EMPTY_REVISION: | 217 | if new_rev == EMPTY_REVISION: |
389 | 204 | new_rev = bzrlib.revision.NULL_REVISION | 218 | new_rev = bzrlib.revision.NULL_REVISION |
390 | 205 | self.generate_revision_history(new_rev) | 219 | self.generate_revision_history(new_rev) |
391 | 206 | return | 220 | return |
392 | 207 | # non-end thread removed. | 221 | # non-end thread removed. |
394 | 208 | self.nick = threads[current_index][0] | 222 | self._set_nick(threads[current_index][0]) |
395 | 209 | new_rev = threads[current_index][1] | 223 | new_rev = threads[current_index][1] |
396 | 210 | if new_rev == EMPTY_REVISION: | 224 | if new_rev == EMPTY_REVISION: |
397 | 211 | new_rev = bzrlib.revision.NULL_REVISION | 225 | new_rev = bzrlib.revision.NULL_REVISION |
398 | @@ -229,85 +243,25 @@ | |||
399 | 229 | def clone(self, to_bzrdir, revision_id=None, repository_policy=None): | 243 | def clone(self, to_bzrdir, revision_id=None, repository_policy=None): |
400 | 230 | """Clone the branch into to_bzrdir. | 244 | """Clone the branch into to_bzrdir. |
401 | 231 | 245 | ||
404 | 232 | This differs from the base clone by cloning the loom and | 246 | This differs from the base clone by cloning the loom, setting the |
405 | 233 | setting the current nick to the top of the loom. | 247 | current nick to the top of the loom, not honouring any branch format |
406 | 248 | selection on the target bzrdir, and ensuring that the format of | ||
407 | 249 | the created branch is stacking compatible. | ||
408 | 234 | """ | 250 | """ |
410 | 235 | result = self._format.initialize(to_bzrdir) | 251 | # If the target is a stackable repository, force-upgrade the |
411 | 252 | # output loom format | ||
412 | 253 | if (isinstance(to_bzrdir, bzrdir.BzrDirMeta1) and | ||
413 | 254 | to_bzrdir._format.repository_format.supports_external_lookups): | ||
414 | 255 | format = BzrBranchLoomFormat7() | ||
415 | 256 | else: | ||
416 | 257 | format = self._format | ||
417 | 258 | result = format.initialize(to_bzrdir) | ||
418 | 236 | if repository_policy is not None: | 259 | if repository_policy is not None: |
419 | 237 | repository_policy.configure_branch(result) | 260 | repository_policy.configure_branch(result) |
421 | 238 | self.copy_content_into(result, revision_id=revision_id) | 261 | bzrlib.branch.InterBranch.get(self, result).copy_content_into( |
422 | 262 | revision_id=revision_id) | ||
423 | 239 | return result | 263 | return result |
424 | 240 | 264 | ||
425 | 241 | @needs_read_lock | ||
426 | 242 | def copy_content_into(self, destination, revision_id=None): | ||
427 | 243 | # XXX: hint for bzrlib - break this into two routines, one for | ||
428 | 244 | # copying the last-rev pointer, one for copying parent etc. | ||
429 | 245 | destination.lock_write() | ||
430 | 246 | try: | ||
431 | 247 | source_nick = self.nick | ||
432 | 248 | state = self.get_loom_state() | ||
433 | 249 | parents = state.get_parents() | ||
434 | 250 | if parents: | ||
435 | 251 | loom_tip = parents[0] | ||
436 | 252 | else: | ||
437 | 253 | loom_tip = None | ||
438 | 254 | threads = self.get_threads(state.get_basis_revision_id()) | ||
439 | 255 | if revision_id not in (None, NULL_REVISION): | ||
440 | 256 | if threads: | ||
441 | 257 | # revision_id should be in the loom, or its an error | ||
442 | 258 | found_threads = [thread for thread, rev in threads | ||
443 | 259 | if rev == revision_id] | ||
444 | 260 | if not found_threads: | ||
445 | 261 | # the thread we have been asked to set in the remote | ||
446 | 262 | # side has not been recorded yet, so its data is not | ||
447 | 263 | # present at this point. | ||
448 | 264 | raise UnrecordedRevision(self, revision_id) | ||
449 | 265 | |||
450 | 266 | # pull in the warp, which was skipped during the initial pull | ||
451 | 267 | # because the front end does not know what to pull. | ||
452 | 268 | # nb: this is mega huge hacky. THINK. RBC 2006062 | ||
453 | 269 | nested = bzrlib.ui.ui_factory.nested_progress_bar() | ||
454 | 270 | try: | ||
455 | 271 | if parents: | ||
456 | 272 | destination.repository.fetch(self.repository, | ||
457 | 273 | revision_id=parents[0]) | ||
458 | 274 | if threads: | ||
459 | 275 | for thread, rev_id in reversed(threads): | ||
460 | 276 | # fetch the loom content for this revision | ||
461 | 277 | destination.repository.fetch(self.repository, | ||
462 | 278 | revision_id=rev_id) | ||
463 | 279 | finally: | ||
464 | 280 | nested.finished() | ||
465 | 281 | state = loom_state.LoomState() | ||
466 | 282 | try: | ||
467 | 283 | require_loom_branch(destination) | ||
468 | 284 | if threads: | ||
469 | 285 | last_rev = threads[-1][1] | ||
470 | 286 | if last_rev == EMPTY_REVISION: | ||
471 | 287 | last_rev = bzrlib.revision.NULL_REVISION | ||
472 | 288 | destination.generate_revision_history(last_rev) | ||
473 | 289 | state.set_parents([loom_tip]) | ||
474 | 290 | state.set_threads( | ||
475 | 291 | (thread + ([thread[1]],) for thread in threads) | ||
476 | 292 | ) | ||
477 | 293 | else: | ||
478 | 294 | # no threads yet, be a normal branch. | ||
479 | 295 | self._synchronize_history(destination, revision_id) | ||
480 | 296 | destination._set_last_loom(state) | ||
481 | 297 | except NotALoom: | ||
482 | 298 | self._synchronize_history(destination, revision_id) | ||
483 | 299 | try: | ||
484 | 300 | parent = self.get_parent() | ||
485 | 301 | except bzrlib.errors.InaccessibleParent, e: | ||
486 | 302 | bzrlib.trace.mutter('parent was not accessible to copy: %s', e) | ||
487 | 303 | else: | ||
488 | 304 | if parent: | ||
489 | 305 | destination.set_parent(parent) | ||
490 | 306 | if threads: | ||
491 | 307 | destination.nick = threads[-1][0] | ||
492 | 308 | finally: | ||
493 | 309 | destination.unlock() | ||
494 | 310 | |||
495 | 311 | def _get_checkout_format(self): | 265 | def _get_checkout_format(self): |
496 | 312 | """Checking out a Loom gets a regular branch for now. | 266 | """Checking out a Loom gets a regular branch for now. |
497 | 313 | 267 | ||
498 | @@ -435,22 +389,25 @@ | |||
499 | 435 | result.append((name, rev_id)) | 389 | result.append((name, rev_id)) |
500 | 436 | return result | 390 | return result |
501 | 437 | 391 | ||
518 | 438 | @needs_write_lock | 392 | def _loom_get_nick(self): |
519 | 439 | def pull(self, source, overwrite=False, stop_revision=None, | 393 | return self._get_nick(local=True) |
520 | 440 | run_hooks=True, possible_transports=None, _override_hook_target=None): | 394 | |
521 | 441 | """Pull from a branch into this loom. | 395 | def _rename_thread(self, nick): |
522 | 442 | 396 | """Rename the current thread to nick.""" | |
523 | 443 | If the remote branch is a non-loom branch, the pull is done against the | 397 | state = self.get_loom_state() |
524 | 444 | current warp. If it is a loom branch, then the pull is done against the | 398 | threads = state.get_threads() |
525 | 445 | entire loom and the current thread set to the top thread. | 399 | if not len(threads): |
526 | 446 | """ | 400 | # No threads at all - probably a default initialised loom in the |
527 | 447 | if not isinstance(source, LoomSupport): | 401 | # test suite. |
528 | 448 | return super(LoomSupport, self).pull(source, | 402 | return self._set_nick(nick) |
529 | 449 | overwrite=overwrite, stop_revision=stop_revision, | 403 | current_index = state.thread_index(self.nick) |
530 | 450 | possible_transports=possible_transports, | 404 | threads[current_index] = (nick,) + threads[current_index][1:] |
531 | 451 | _override_hook_target=_override_hook_target) | 405 | state.set_threads(threads) |
532 | 452 | return _Puller(source, self).transfer(overwrite, stop_revision, | 406 | self._set_last_loom(state) |
533 | 453 | run_hooks, possible_transports, _override_hook_target) | 407 | # Preserve default behavior: set the branch nick |
534 | 408 | self._set_nick(nick) | ||
535 | 409 | |||
536 | 410 | nick = property(_loom_get_nick, _rename_thread) | ||
537 | 454 | 411 | ||
538 | 455 | @needs_read_lock | 412 | @needs_read_lock |
539 | 456 | def push(self, target, overwrite=False, stop_revision=None, | 413 | def push(self, target, overwrite=False, stop_revision=None, |
540 | @@ -606,10 +563,20 @@ | |||
541 | 606 | 563 | ||
542 | 607 | 564 | ||
543 | 608 | class _Puller(object): | 565 | class _Puller(object): |
544 | 566 | # XXX: Move into InterLoomBranch. | ||
545 | 609 | 567 | ||
546 | 610 | def __init__(self, source, target): | 568 | def __init__(self, source, target): |
547 | 611 | self.target = target | 569 | self.target = target |
548 | 612 | self.source = source | 570 | self.source = source |
549 | 571 | # If _Puller has been created, we need real branch objects. | ||
550 | 572 | self.real_target = self.unwrap_branch(target) | ||
551 | 573 | self.real_source = self.unwrap_branch(source) | ||
552 | 574 | |||
553 | 575 | def unwrap_branch(self, branch): | ||
554 | 576 | if isinstance(branch, remote.RemoteBranch): | ||
555 | 577 | branch._ensure_real() | ||
556 | 578 | return branch._real_branch | ||
557 | 579 | return branch | ||
558 | 613 | 580 | ||
559 | 614 | def prepare_result(self, _override_hook_target): | 581 | def prepare_result(self, _override_hook_target): |
560 | 615 | result = self.make_result() | 582 | result = self.make_result() |
561 | @@ -669,8 +636,10 @@ | |||
562 | 669 | return result | 636 | return result |
563 | 670 | 637 | ||
564 | 671 | def transfer(self, overwrite, stop_revision, run_hooks=True, | 638 | def transfer(self, overwrite, stop_revision, run_hooks=True, |
566 | 672 | possible_transports=None, _override_hook_target=None): | 639 | possible_transports=None, _override_hook_target=None, local=False): |
567 | 673 | """Implementation of push and pull""" | 640 | """Implementation of push and pull""" |
568 | 641 | if local: | ||
569 | 642 | raise bzrlib.errors.LocalRequiresBoundBranch() | ||
570 | 674 | # pull the loom, and position our | 643 | # pull the loom, and position our |
571 | 675 | pb = bzrlib.ui.ui_factory.nested_progress_bar() | 644 | pb = bzrlib.ui.ui_factory.nested_progress_bar() |
572 | 676 | try: | 645 | try: |
573 | @@ -678,7 +647,7 @@ | |||
574 | 678 | self.target.lock_write() | 647 | self.target.lock_write() |
575 | 679 | self.source.lock_read() | 648 | self.source.lock_read() |
576 | 680 | try: | 649 | try: |
578 | 681 | source_state = self.source.get_loom_state() | 650 | source_state = self.real_source.get_loom_state() |
579 | 682 | source_parents = source_state.get_parents() | 651 | source_parents = source_state.get_parents() |
580 | 683 | if not source_parents: | 652 | if not source_parents: |
581 | 684 | return self.plain_transfer(result, run_hooks, | 653 | return self.plain_transfer(result, run_hooks, |
582 | @@ -724,7 +693,7 @@ | |||
583 | 724 | # and save the state. | 693 | # and save the state. |
584 | 725 | self.target._set_last_loom(my_state) | 694 | self.target._set_last_loom(my_state) |
585 | 726 | # set the branch nick. | 695 | # set the branch nick. |
587 | 727 | self.target.nick = threads[-1][0] | 696 | self.target._set_nick(threads[-1][0]) |
588 | 728 | # and position the branch on the top loom | 697 | # and position the branch on the top loom |
589 | 729 | new_rev = threads[-1][1] | 698 | new_rev = threads[-1][1] |
590 | 730 | if new_rev == EMPTY_REVISION: | 699 | if new_rev == EMPTY_REVISION: |
591 | @@ -743,7 +712,7 @@ | |||
592 | 743 | 712 | ||
593 | 744 | @staticmethod | 713 | @staticmethod |
594 | 745 | def make_result(): | 714 | def make_result(): |
596 | 746 | return bzrlib.branch.PushResult() | 715 | return bzrlib.branch.BranchPushResult() |
597 | 747 | 716 | ||
598 | 748 | @staticmethod | 717 | @staticmethod |
599 | 749 | def post_hooks(): | 718 | def post_hooks(): |
600 | @@ -780,9 +749,11 @@ | |||
601 | 780 | # A mixin is not ideal because it is tricky to test, but it seems to be the | 749 | # A mixin is not ideal because it is tricky to test, but it seems to be the |
602 | 781 | # best solution for now. | 750 | # best solution for now. |
603 | 782 | 751 | ||
605 | 783 | def initialize(self, a_bzrdir): | 752 | def initialize(self, a_bzrdir, name=None): |
606 | 784 | """Create a branch of this format in a_bzrdir.""" | 753 | """Create a branch of this format in a_bzrdir.""" |
608 | 785 | super(LoomFormatMixin, self).initialize(a_bzrdir) | 754 | if name is not None: |
609 | 755 | raise bzrlib.errors.NoColocatedBranchSupport(self) | ||
610 | 756 | super(LoomFormatMixin, self).initialize(a_bzrdir, name=None) | ||
611 | 786 | branch_transport = a_bzrdir.get_branch_transport(self) | 757 | branch_transport = a_bzrdir.get_branch_transport(self) |
612 | 787 | files = [] | 758 | files = [] |
613 | 788 | state = loom_state.LoomState() | 759 | state = loom_state.LoomState() |
614 | @@ -801,15 +772,21 @@ | |||
615 | 801 | control_files.unlock() | 772 | control_files.unlock() |
616 | 802 | return self.open(a_bzrdir, _found=True, ) | 773 | return self.open(a_bzrdir, _found=True, ) |
617 | 803 | 774 | ||
619 | 804 | def open(self, a_bzrdir, _found=False, ignore_fallbacks=False): | 775 | def open(self, a_bzrdir, name=None, _found=False, ignore_fallbacks=False): |
620 | 805 | """Return the branch object for a_bzrdir | 776 | """Return the branch object for a_bzrdir |
621 | 806 | 777 | ||
622 | 807 | _found is a private parameter, do not use it. It is used to indicate | 778 | _found is a private parameter, do not use it. It is used to indicate |
623 | 808 | if format probing has already be done. | 779 | if format probing has already be done. |
624 | 780 | |||
625 | 781 | :param name: The 'colocated branches' name for the branch to open. | ||
626 | 782 | in future, Loom may use that to return a Thread, but for now | ||
627 | 783 | it is unused. | ||
628 | 809 | """ | 784 | """ |
629 | 810 | if not _found: | 785 | if not _found: |
630 | 811 | format = BranchFormat.find_format(a_bzrdir) | 786 | format = BranchFormat.find_format(a_bzrdir) |
631 | 812 | assert format.__class__ == self.__class__ | 787 | assert format.__class__ == self.__class__ |
632 | 788 | if name is not None: | ||
633 | 789 | raise bzrlib.errors.NoColocatedBranchSupport(self) | ||
634 | 813 | transport = a_bzrdir.get_branch_transport(None) | 790 | transport = a_bzrdir.get_branch_transport(None) |
635 | 814 | control_files = bzrlib.lockable_files.LockableFiles( | 791 | control_files = bzrlib.lockable_files.LockableFiles( |
636 | 815 | transport, 'lock', bzrlib.lockdir.LockDir) | 792 | transport, 'lock', bzrlib.lockdir.LockDir) |
637 | @@ -923,13 +900,136 @@ | |||
638 | 923 | return "bzr loom format 7 (based on bzr branch format 7)\n" | 900 | return "bzr loom format 7 (based on bzr branch format 7)\n" |
639 | 924 | 901 | ||
640 | 925 | 902 | ||
651 | 926 | bzrlib.branch.BranchFormat.register_format(BzrBranchLoomFormat1()) | 903 | # Handle the smart server: |
652 | 927 | bzrlib.branch.BranchFormat.register_format(BzrBranchLoomFormat6()) | 904 | |
653 | 928 | bzrlib.branch.BranchFormat.register_format(BzrBranchLoomFormat7()) | 905 | class InterLoomBranch(bzrlib.branch.GenericInterBranch): |
654 | 929 | 906 | ||
655 | 930 | 907 | @classmethod | |
656 | 931 | LOOM_FORMATS = [ | 908 | def _get_branch_formats_to_test(klass): |
657 | 932 | BzrBranchLoomFormat1, | 909 | return [ |
658 | 933 | BzrBranchLoomFormat6, | 910 | (bzrlib.branch.BranchFormat._default_format, |
659 | 934 | BzrBranchLoomFormat7, | 911 | BzrBranchLoomFormat7()), |
660 | 935 | ] | 912 | (BzrBranchLoomFormat7(), |
661 | 913 | bzrlib.branch.BranchFormat._default_format), | ||
662 | 914 | (BzrBranchLoomFormat7(), BzrBranchLoomFormat7()), | ||
663 | 915 | ] | ||
664 | 916 | |||
665 | 917 | def unwrap_branch(self, branch): | ||
666 | 918 | if isinstance(branch, remote.RemoteBranch): | ||
667 | 919 | branch._ensure_real() | ||
668 | 920 | return branch._real_branch | ||
669 | 921 | return branch | ||
670 | 922 | |||
671 | 923 | @classmethod | ||
672 | 924 | def is_compatible(klass, source, target): | ||
673 | 925 | # 1st cut: special case and handle all *->Loom and Loom->* | ||
674 | 926 | return klass.branch_is_loom(source) or klass.branch_is_loom(target) | ||
675 | 927 | |||
676 | 928 | def get_loom_state(self, branch): | ||
677 | 929 | branch = self.unwrap_branch(branch) | ||
678 | 930 | return branch.get_loom_state() | ||
679 | 931 | |||
680 | 932 | def get_threads(self, branch, revision_id): | ||
681 | 933 | branch = self.unwrap_branch(branch) | ||
682 | 934 | return branch.get_threads(revision_id) | ||
683 | 935 | |||
684 | 936 | @classmethod | ||
685 | 937 | def branch_is_loom(klass, branch): | ||
686 | 938 | format = klass.unwrap_format(branch._format) | ||
687 | 939 | return isinstance(format, LoomFormatMixin) | ||
688 | 940 | |||
689 | 941 | @needs_write_lock | ||
690 | 942 | def copy_content_into(self, revision_id=None): | ||
691 | 943 | if not self.__class__.branch_is_loom(self.source): | ||
692 | 944 | # target is loom, but the generic code path works Just Fine for | ||
693 | 945 | # regular to loom copy_content_into. | ||
694 | 946 | return super(InterLoomBranch, self).copy_content_into( | ||
695 | 947 | revision_id=revision_id) | ||
696 | 948 | # XXX: hint for bzrlib - break this into two routines, one for | ||
697 | 949 | # copying the last-rev pointer, one for copying parent etc. | ||
698 | 950 | source_nick = self.source.nick | ||
699 | 951 | state = self.get_loom_state(self.source) | ||
700 | 952 | parents = state.get_parents() | ||
701 | 953 | if parents: | ||
702 | 954 | loom_tip = parents[0] | ||
703 | 955 | else: | ||
704 | 956 | loom_tip = None | ||
705 | 957 | threads = self.get_threads(self.source, state.get_basis_revision_id()) | ||
706 | 958 | if revision_id not in (None, NULL_REVISION): | ||
707 | 959 | if threads: | ||
708 | 960 | # revision_id should be in the loom, or its an error | ||
709 | 961 | found_threads = [thread for thread, rev in threads | ||
710 | 962 | if rev == revision_id] | ||
711 | 963 | if not found_threads: | ||
712 | 964 | # the thread we have been asked to set in the remote | ||
713 | 965 | # side has not been recorded yet, so its data is not | ||
714 | 966 | # present at this point. | ||
715 | 967 | raise UnrecordedRevision(self.source, revision_id) | ||
716 | 968 | |||
717 | 969 | # pull in the warp, which was skipped during the initial pull | ||
718 | 970 | # because the front end does not know what to pull. | ||
719 | 971 | # nb: this is mega huge hacky. THINK. RBC 2006062 | ||
720 | 972 | nested = bzrlib.ui.ui_factory.nested_progress_bar() | ||
721 | 973 | try: | ||
722 | 974 | if parents: | ||
723 | 975 | self.target.repository.fetch(self.source.repository, | ||
724 | 976 | revision_id=parents[0]) | ||
725 | 977 | if threads: | ||
726 | 978 | for thread, rev_id in reversed(threads): | ||
727 | 979 | # fetch the loom content for this revision | ||
728 | 980 | self.target.repository.fetch(self.source.repository, | ||
729 | 981 | revision_id=rev_id) | ||
730 | 982 | finally: | ||
731 | 983 | nested.finished() | ||
732 | 984 | state = loom_state.LoomState() | ||
733 | 985 | try: | ||
734 | 986 | require_loom_branch(self.target) | ||
735 | 987 | if threads: | ||
736 | 988 | last_rev = threads[-1][1] | ||
737 | 989 | if last_rev == EMPTY_REVISION: | ||
738 | 990 | last_rev = bzrlib.revision.NULL_REVISION | ||
739 | 991 | self.target.generate_revision_history(last_rev) | ||
740 | 992 | state.set_parents([loom_tip]) | ||
741 | 993 | state.set_threads( | ||
742 | 994 | (thread + ([thread[1]],) for thread in threads) | ||
743 | 995 | ) | ||
744 | 996 | else: | ||
745 | 997 | # no threads yet, be a normal branch. | ||
746 | 998 | self.source._synchronize_history(self.target, revision_id) | ||
747 | 999 | target_loom = self.unwrap_branch(self.target) | ||
748 | 1000 | target_loom._set_last_loom(state) | ||
749 | 1001 | except NotALoom: | ||
750 | 1002 | self.source._synchronize_history(self.target, revision_id) | ||
751 | 1003 | try: | ||
752 | 1004 | parent = self.source.get_parent() | ||
753 | 1005 | except bzrlib.errors.InaccessibleParent, e: | ||
754 | 1006 | bzrlib.trace.mutter('parent was not accessible to copy: %s', e) | ||
755 | 1007 | else: | ||
756 | 1008 | if parent: | ||
757 | 1009 | self.target.set_parent(parent) | ||
758 | 1010 | if threads: | ||
759 | 1011 | self.target._set_nick(threads[-1][0]) | ||
760 | 1012 | |||
761 | 1013 | @needs_write_lock | ||
762 | 1014 | def pull(self, overwrite=False, stop_revision=None, | ||
763 | 1015 | run_hooks=True, possible_transports=None, _override_hook_target=None, | ||
764 | 1016 | local=False): | ||
765 | 1017 | """Perform a pull, reading from self.source and writing to self.target. | ||
766 | 1018 | |||
767 | 1019 | If the source branch is a non-loom branch, the pull is done against the | ||
768 | 1020 | current warp. If it is a loom branch, then the pull is done against the | ||
769 | 1021 | entire loom and the current thread set to the top thread. | ||
770 | 1022 | """ | ||
771 | 1023 | # Special code only needed when both source and targets are looms: | ||
772 | 1024 | if (self.__class__.branch_is_loom(self.target) and | ||
773 | 1025 | self.__class__.branch_is_loom(self.source)): | ||
774 | 1026 | return _Puller(self.source, self.target).transfer(overwrite, stop_revision, | ||
775 | 1027 | run_hooks, possible_transports, _override_hook_target, local) | ||
776 | 1028 | return super(InterLoomBranch, self).pull( | ||
777 | 1029 | overwrite=overwrite, stop_revision=stop_revision, | ||
778 | 1030 | possible_transports=possible_transports, | ||
779 | 1031 | _override_hook_target=_override_hook_target, local=local, | ||
780 | 1032 | run_hooks=run_hooks) | ||
781 | 1033 | |||
782 | 1034 | |||
783 | 1035 | bzrlib.branch.InterBranch.register_optimiser(InterLoomBranch) | ||
784 | 936 | 1036 | ||
785 | === modified file 'commands.py' | |||
786 | --- commands.py 2009-02-13 02:30:24 +0000 | |||
787 | +++ commands.py 2010-08-05 18:57:44 +0000 | |||
788 | @@ -17,18 +17,23 @@ | |||
789 | 17 | 17 | ||
790 | 18 | """Loom commands.""" | 18 | """Loom commands.""" |
791 | 19 | 19 | ||
793 | 20 | from bzrlib import workingtree | 20 | from bzrlib import bzrdir, directory_service, workingtree |
794 | 21 | import bzrlib.commands | 21 | import bzrlib.commands |
795 | 22 | import bzrlib.branch | 22 | import bzrlib.branch |
796 | 23 | from bzrlib import errors | 23 | from bzrlib import errors |
797 | 24 | from bzrlib.lazy_import import lazy_import | ||
798 | 24 | import bzrlib.merge | 25 | import bzrlib.merge |
799 | 25 | from bzrlib.option import Option | 26 | from bzrlib.option import Option |
800 | 26 | import bzrlib.revision | 27 | import bzrlib.revision |
801 | 27 | import bzrlib.trace | 28 | import bzrlib.trace |
802 | 28 | import bzrlib.transport | 29 | import bzrlib.transport |
803 | 29 | 30 | ||
804 | 31 | import formats | ||
805 | 32 | |||
806 | 33 | lazy_import(globals(), """ | ||
807 | 30 | import branch | 34 | import branch |
808 | 31 | from tree import LoomTreeDecorator | 35 | from tree import LoomTreeDecorator |
809 | 36 | """) | ||
810 | 32 | 37 | ||
811 | 33 | 38 | ||
812 | 34 | class cmd_loomify(bzrlib.commands.Command): | 39 | class cmd_loomify(bzrlib.commands.Command): |
813 | @@ -67,7 +72,7 @@ | |||
814 | 67 | 72 | ||
815 | 68 | 73 | ||
816 | 69 | class cmd_combine_thread(bzrlib.commands.Command): | 74 | class cmd_combine_thread(bzrlib.commands.Command): |
818 | 70 | """Combine the current thread with the thread below it. | 75 | __doc__ = """Combine the current thread with the thread below it. |
819 | 71 | 76 | ||
820 | 72 | This will currently refuse to operate on the last thread, but in the future | 77 | This will currently refuse to operate on the last thread, but in the future |
821 | 73 | will just turn the loom into a normal branch again. | 78 | will just turn the loom into a normal branch again. |
822 | @@ -79,23 +84,53 @@ | |||
823 | 79 | * Change threads to the thread below. | 84 | * Change threads to the thread below. |
824 | 80 | """ | 85 | """ |
825 | 81 | 86 | ||
827 | 82 | def run(self): | 87 | takes_options = [ |
828 | 88 | Option('force', help='Combine even if work in the thread is not ' | ||
829 | 89 | 'integrated up or down the loom.'), | ||
830 | 90 | ] | ||
831 | 91 | |||
832 | 92 | def run(self, force=False): | ||
833 | 83 | (tree, path) = workingtree.WorkingTree.open_containing('.') | 93 | (tree, path) = workingtree.WorkingTree.open_containing('.') |
834 | 84 | branch.require_loom_branch(tree.branch) | 94 | branch.require_loom_branch(tree.branch) |
839 | 85 | tree.lock_write() | 95 | self.add_cleanup(tree.lock_write().unlock) |
840 | 86 | try: | 96 | current_thread = tree.branch.nick |
841 | 87 | current_thread = tree.branch.nick | 97 | state = tree.branch.get_loom_state() |
842 | 88 | state = tree.branch.get_loom_state() | 98 | if not force: |
843 | 99 | # Check for unmerged work. | ||
844 | 100 | # XXX: Layering issue whom should be caring for the check, not the | ||
845 | 101 | # command thats for sure. | ||
846 | 89 | threads = state.get_threads() | 102 | threads = state.get_threads() |
856 | 90 | new_thread = state.get_new_thread_after_deleting(current_thread) | 103 | current_index = state.thread_index(current_thread) |
857 | 91 | if new_thread is None: | 104 | rev_below = None |
858 | 92 | raise branch.CannotCombineOnLastThread | 105 | rev_current = threads[current_index][1] |
859 | 93 | bzrlib.trace.note("Combining thread '%s' into '%s'", | 106 | rev_above = None |
860 | 94 | current_thread, new_thread) | 107 | if current_index: |
861 | 95 | LoomTreeDecorator(tree).down_thread(new_thread) | 108 | # There is a thread below |
862 | 96 | tree.branch.remove_thread(current_thread) | 109 | rev_below = threads[current_index - 1][1] |
863 | 97 | finally: | 110 | if current_index < len(threads) - 1: |
864 | 98 | tree.unlock() | 111 | rev_above = threads[current_index + 1][1] |
865 | 112 | graph = tree.branch.repository.get_graph() | ||
866 | 113 | candidates = [rev for rev in | ||
867 | 114 | (rev_below, rev_current, rev_above) if rev] | ||
868 | 115 | heads = graph.heads(candidates) | ||
869 | 116 | # If current is not a head, its trivially merged, or | ||
870 | 117 | # if current is == rev_below, its also merged, or | ||
871 | 118 | # if there is only one thread its merged (well its not unmerged). | ||
872 | 119 | if (rev_current == rev_below or rev_current not in heads or | ||
873 | 120 | (rev_below is None and rev_above is None)): | ||
874 | 121 | merged = True | ||
875 | 122 | else: | ||
876 | 123 | merged = False | ||
877 | 124 | if not merged: | ||
878 | 125 | raise errors.BzrCommandError("Thread '%s' has unmerged work" | ||
879 | 126 | ". Use --force to combine anyway." % current_thread) | ||
880 | 127 | new_thread = state.get_new_thread_after_deleting(current_thread) | ||
881 | 128 | if new_thread is None: | ||
882 | 129 | raise branch.CannotCombineOnLastThread | ||
883 | 130 | bzrlib.trace.note("Combining thread '%s' into '%s'", | ||
884 | 131 | current_thread, new_thread) | ||
885 | 132 | LoomTreeDecorator(tree).down_thread(new_thread) | ||
886 | 133 | tree.branch.remove_thread(current_thread) | ||
887 | 99 | 134 | ||
888 | 100 | 135 | ||
889 | 101 | class cmd_create_thread(bzrlib.commands.Command): | 136 | class cmd_create_thread(bzrlib.commands.Command): |
890 | @@ -106,19 +141,15 @@ | |||
891 | 106 | 141 | ||
892 | 107 | The thread-name must be a valid branch 'nickname', and must not be the name | 142 | The thread-name must be a valid branch 'nickname', and must not be the name |
893 | 108 | of an existing thread in your loom. | 143 | of an existing thread in your loom. |
894 | 144 | |||
895 | 145 | The new thread is created immediately after the current thread. | ||
896 | 109 | """ | 146 | """ |
897 | 110 | 147 | ||
898 | 111 | takes_args = ['thread'] | 148 | takes_args = ['thread'] |
899 | 112 | 149 | ||
900 | 113 | def run(self, thread): | 150 | def run(self, thread): |
901 | 114 | (loom, path) = bzrlib.branch.Branch.open_containing('.') | 151 | (loom, path) = bzrlib.branch.Branch.open_containing('.') |
909 | 115 | branch.require_loom_branch(loom) | 152 | branch.create_thread(loom, thread) |
903 | 116 | loom.lock_write() | ||
904 | 117 | try: | ||
905 | 118 | loom.new_thread(thread, loom.nick) | ||
906 | 119 | loom.nick = thread | ||
907 | 120 | finally: | ||
908 | 121 | loom.unlock() | ||
910 | 122 | 153 | ||
911 | 123 | 154 | ||
912 | 124 | class cmd_show_loom(bzrlib.commands.Command): | 155 | class cmd_show_loom(bzrlib.commands.Command): |
913 | @@ -160,7 +191,7 @@ | |||
914 | 160 | else: | 191 | else: |
915 | 161 | path = file_list[0] | 192 | path = file_list[0] |
916 | 162 | (loom, _) = bzrlib.branch.Branch.open_containing(path) | 193 | (loom, _) = bzrlib.branch.Branch.open_containing(path) |
918 | 163 | branch.require_loom_branch(loom) | 194 | formats.require_loom_branch(loom) |
919 | 164 | loom.lock_read() | 195 | loom.lock_read() |
920 | 165 | try: | 196 | try: |
921 | 166 | print 'Current thread: %s' % loom.nick | 197 | print 'Current thread: %s' % loom.nick |
922 | @@ -172,7 +203,7 @@ | |||
923 | 172 | self._original_command().run_argv_aliases(argv, alias_argv) | 203 | self._original_command().run_argv_aliases(argv, alias_argv) |
924 | 173 | try: | 204 | try: |
925 | 174 | super(cmd_status, self).run_argv_aliases(list(argv), alias_argv) | 205 | super(cmd_status, self).run_argv_aliases(list(argv), alias_argv) |
927 | 175 | except branch.NotALoom: | 206 | except formats.NotALoom: |
928 | 176 | pass | 207 | pass |
929 | 177 | 208 | ||
930 | 178 | 209 | ||
931 | @@ -208,15 +239,43 @@ | |||
932 | 208 | return thread[0] | 239 | return thread[0] |
933 | 209 | return to_location | 240 | return to_location |
934 | 210 | 241 | ||
938 | 211 | def run(self, to_location, force=False): | 242 | def run(self, to_location=None, force=False, create_branch=False, |
939 | 212 | (tree, path) = workingtree.WorkingTree.open_containing('.') | 243 | revision=None, directory=None): |
940 | 213 | tree = LoomTreeDecorator(tree) | 244 | # The top of this is cribbed from bzr; because bzr isn't factored out |
941 | 245 | # enough. | ||
942 | 246 | if directory is None: | ||
943 | 247 | directory = u'.' | ||
944 | 248 | control_dir, path = bzrdir.BzrDir.open_containing(directory) | ||
945 | 249 | if to_location is None: | ||
946 | 250 | if revision is None: | ||
947 | 251 | raise errors.BzrCommandError( | ||
948 | 252 | 'You must supply either a revision or a location') | ||
949 | 253 | to_location = '.' | ||
950 | 214 | try: | 254 | try: |
956 | 215 | thread_name = self._get_thread_name(tree.branch, to_location) | 255 | from_branch = control_dir.open_branch() |
957 | 216 | return tree.down_thread(thread_name) | 256 | except errors.NotBranchError: |
958 | 217 | except (AttributeError, branch.NoSuchThread): | 257 | from_branch = None |
959 | 218 | # When there is no thread its probably an external branch | 258 | if create_branch: |
960 | 219 | # that we have been given. | 259 | if from_branch is None: |
961 | 260 | raise errors.BzrCommandError( | ||
962 | 261 | 'cannot create branch without source branch') | ||
963 | 262 | to_location = directory_service.directories.dereference( | ||
964 | 263 | to_location) | ||
965 | 264 | if from_branch is not None: | ||
966 | 265 | # Note: reopens. | ||
967 | 266 | (tree, path) = workingtree.WorkingTree.open_containing(directory) | ||
968 | 267 | tree = LoomTreeDecorator(tree) | ||
969 | 268 | try: | ||
970 | 269 | if create_branch: | ||
971 | 270 | return branch.create_thread(tree.branch, to_location) | ||
972 | 271 | thread_name = self._get_thread_name(tree.branch, to_location) | ||
973 | 272 | return tree.down_thread(thread_name) | ||
974 | 273 | except (AttributeError, branch.NoSuchThread, branch.NotALoom): | ||
975 | 274 | # When there is no thread its probably an external branch | ||
976 | 275 | # that we have been given. | ||
977 | 276 | raise errors.MustUseDecorated | ||
978 | 277 | else: | ||
979 | 278 | # switching to a relocated branch | ||
980 | 220 | raise errors.MustUseDecorated | 279 | raise errors.MustUseDecorated |
981 | 221 | 280 | ||
982 | 222 | def run_argv_aliases(self, argv, alias_argv=None): | 281 | def run_argv_aliases(self, argv, alias_argv=None): |
983 | @@ -307,26 +366,34 @@ | |||
984 | 307 | 366 | ||
985 | 308 | 367 | ||
986 | 309 | class cmd_up_thread(bzrlib.commands.Command): | 368 | class cmd_up_thread(bzrlib.commands.Command): |
989 | 310 | """Move the branch up a thread in the loom. | 369 | """Move the branch up to the top thread in the loom. |
990 | 311 | 370 | ||
991 | 312 | This merges the changes done in this thread but not incorporated into | 371 | This merges the changes done in this thread but not incorporated into |
992 | 313 | the next thread up into the next thread up and switches your tree to be | 372 | the next thread up into the next thread up and switches your tree to be |
994 | 314 | that thread. | 373 | that thread. Unless there are conflicts, or --manual is specified, it |
995 | 374 | will then commit and repeat the process. | ||
996 | 315 | """ | 375 | """ |
997 | 316 | 376 | ||
998 | 377 | takes_args = ['thread?'] | ||
999 | 378 | |||
1000 | 317 | takes_options = ['merge-type', Option('auto', | 379 | takes_options = ['merge-type', Option('auto', |
1002 | 318 | help='Automatically commit and merge repeatedly.')] | 380 | help='Deprecated - now the default.'), |
1003 | 381 | Option('manual', help='Perform commit manually.'), | ||
1004 | 382 | ] | ||
1005 | 319 | 383 | ||
1006 | 320 | _see_also = ['down-thread', 'switch'] | 384 | _see_also = ['down-thread', 'switch'] |
1007 | 321 | 385 | ||
1009 | 322 | def run(self, merge_type=None, auto=False): | 386 | def run(self, merge_type=None, manual=False, thread=None, auto=None): |
1010 | 323 | (tree, path) = workingtree.WorkingTree.open_containing('.') | 387 | (tree, path) = workingtree.WorkingTree.open_containing('.') |
1011 | 324 | branch.require_loom_branch(tree.branch) | 388 | branch.require_loom_branch(tree.branch) |
1012 | 325 | tree = LoomTreeDecorator(tree) | 389 | tree = LoomTreeDecorator(tree) |
1014 | 326 | if not auto: | 390 | if manual: |
1015 | 391 | if thread is not None: | ||
1016 | 392 | raise errors.BzrCommandError('Specifying a thread does not' | ||
1017 | 393 | ' work with --manual.') | ||
1018 | 327 | return tree.up_thread(merge_type) | 394 | return tree.up_thread(merge_type) |
1019 | 328 | else: | 395 | else: |
1021 | 329 | return tree.up_many(merge_type) | 396 | return tree.up_many(merge_type, thread) |
1022 | 330 | 397 | ||
1023 | 331 | 398 | ||
1024 | 332 | class cmd_export_loom(bzrlib.commands.Command): | 399 | class cmd_export_loom(bzrlib.commands.Command): |
1025 | 333 | 400 | ||
1026 | === added file 'formats.py' | |||
1027 | --- formats.py 1970-01-01 00:00:00 +0000 | |||
1028 | +++ formats.py 2010-08-05 18:57:44 +0000 | |||
1029 | @@ -0,0 +1,74 @@ | |||
1030 | 1 | # Loom, a plugin for bzr to assist in developing focused patches. | ||
1031 | 2 | # Copyright (C) 2010 Canonical Limited. | ||
1032 | 3 | # | ||
1033 | 4 | # This program is free software; you can redistribute it and/or modify | ||
1034 | 5 | # it under the terms of the GNU General Public License version 2 as published | ||
1035 | 6 | # by the Free Software Foundation. | ||
1036 | 7 | # | ||
1037 | 8 | # This program is distributed in the hope that it will be useful, | ||
1038 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1039 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1040 | 11 | # GNU General Public License for more details. | ||
1041 | 12 | # | ||
1042 | 13 | # You should have received a copy of the GNU General Public License | ||
1043 | 14 | # along with this program; if not, write to the Free Software | ||
1044 | 15 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
1045 | 16 | # | ||
1046 | 17 | |||
1047 | 18 | """Format information about formats for Loom. | ||
1048 | 19 | |||
1049 | 20 | This is split out from the implementation of the formats to permit lazy | ||
1050 | 21 | loading without requiring the implementation code to be cryptic. | ||
1051 | 22 | """ | ||
1052 | 23 | |||
1053 | 24 | __all__ = [ | ||
1054 | 25 | 'NotALoom', | ||
1055 | 26 | 'register_formats', | ||
1056 | 27 | 'require_loom_branch', | ||
1057 | 28 | ] | ||
1058 | 29 | |||
1059 | 30 | from bzrlib.lazy_import import lazy_import | ||
1060 | 31 | import bzrlib.errors | ||
1061 | 32 | |||
1062 | 33 | lazy_import(globals(), """ | ||
1063 | 34 | from bzrlib import branch as _mod_branch | ||
1064 | 35 | """) | ||
1065 | 36 | |||
1066 | 37 | |||
1067 | 38 | _LOOM_FORMATS = { | ||
1068 | 39 | "Bazaar-NG Loom branch format 1\n": "BzrBranchLoomFormat1", | ||
1069 | 40 | "Bazaar-NG Loom branch format 6\n": "BzrBranchLoomFormat6", | ||
1070 | 41 | "Bazaar-NG Loom branch format 7\n": "BzrBranchLoomFormat7", | ||
1071 | 42 | } | ||
1072 | 43 | |||
1073 | 44 | def register_formats(): | ||
1074 | 45 | if getattr(_mod_branch, 'MetaDirBranchFormatFactory', None): | ||
1075 | 46 | branch_formats = [_mod_branch.MetaDirBranchFormatFactory(format_string, | ||
1076 | 47 | "bzrlib.plugins.loom.branch", format_class) for | ||
1077 | 48 | (format_string, format_class) in _LOOM_FORMATS.iteritems()] | ||
1078 | 49 | else: | ||
1079 | 50 | # Compat for folk not running bleeding edge. Like me as I commit this. | ||
1080 | 51 | import branch | ||
1081 | 52 | branch_formats = [ | ||
1082 | 53 | branch.BzrBranchLoomFormat1(), | ||
1083 | 54 | branch.BzrBranchLoomFormat6(), | ||
1084 | 55 | branch.BzrBranchLoomFormat7(), | ||
1085 | 56 | ] | ||
1086 | 57 | map(_mod_branch.BranchFormat.register_format, branch_formats) | ||
1087 | 58 | |||
1088 | 59 | |||
1089 | 60 | def require_loom_branch(branch): | ||
1090 | 61 | """Return None if branch is already loomified, or raise NotALoom.""" | ||
1091 | 62 | if branch._format.network_name() not in _LOOM_FORMATS: | ||
1092 | 63 | raise NotALoom(branch) | ||
1093 | 64 | |||
1094 | 65 | |||
1095 | 66 | # TODO: define errors without importing all errors. | ||
1096 | 67 | class NotALoom(bzrlib.errors.BzrError): | ||
1097 | 68 | |||
1098 | 69 | _fmt = ("The branch %(branch)s is not a loom. " | ||
1099 | 70 | "You can use 'bzr loomify' to make it into a loom.") | ||
1100 | 71 | |||
1101 | 72 | def __init__(self, branch): | ||
1102 | 73 | bzrlib.errors.BzrError.__init__(self) | ||
1103 | 74 | self.branch = branch | ||
1104 | 0 | 75 | ||
1105 | === modified file 'revspec.py' | |||
1106 | --- revspec.py 2009-02-13 02:30:24 +0000 | |||
1107 | +++ revspec.py 2010-08-05 18:57:44 +0000 | |||
1108 | @@ -18,52 +18,87 @@ | |||
1109 | 18 | """Loom specific revision-specifiers.""" | 18 | """Loom specific revision-specifiers.""" |
1110 | 19 | 19 | ||
1111 | 20 | 20 | ||
1112 | 21 | from bzrlib import revisionspec | ||
1113 | 22 | from bzrlib.plugins.loom.branch import NoLowerThread | 21 | from bzrlib.plugins.loom.branch import NoLowerThread |
1114 | 22 | from bzrlib.plugins.loom.formats import require_loom_branch | ||
1115 | 23 | from bzrlib.revisionspec import RevisionSpec, RevisionInfo | 23 | from bzrlib.revisionspec import RevisionSpec, RevisionInfo |
1116 | 24 | 24 | ||
1117 | 25 | 25 | ||
1134 | 26 | class RevisionSpecThread(RevisionSpec): | 26 | class LoomRevisionSpec(RevisionSpec): |
1135 | 27 | """The thread: revision specifier.""" | 27 | """A revision spec that needs a loom.""" |
1120 | 28 | |||
1121 | 29 | help_txt = """Selects the tip of a revision from a loom. | ||
1122 | 30 | |||
1123 | 31 | Selects the tip of a thread in a loom. | ||
1124 | 32 | |||
1125 | 33 | Examples:: | ||
1126 | 34 | |||
1127 | 35 | thread: -> return the tip of the next lower thread. | ||
1128 | 36 | thread:foo -> return the tip of the thread named 'foo' | ||
1129 | 37 | |||
1130 | 38 | see also: loom | ||
1131 | 39 | """ | ||
1132 | 40 | |||
1133 | 41 | prefix = 'thread:' | ||
1136 | 42 | 28 | ||
1137 | 43 | def _match_on(self, branch, revs): | 29 | def _match_on(self, branch, revs): |
1138 | 44 | return RevisionInfo(branch, None, self._as_revision_id(branch)) | 30 | return RevisionInfo(branch, None, self._as_revision_id(branch)) |
1139 | 45 | 31 | ||
1140 | 46 | def _as_revision_id(self, branch): | 32 | def _as_revision_id(self, branch): |
1143 | 47 | # '' -> next lower | 33 | require_loom_branch(branch) |
1142 | 48 | # foo -> named | ||
1144 | 49 | branch.lock_read() | 34 | branch.lock_read() |
1145 | 50 | try: | 35 | try: |
1146 | 51 | state = branch.get_loom_state() | 36 | state = branch.get_loom_state() |
1147 | 52 | threads = state.get_threads() | 37 | threads = state.get_threads() |
1156 | 53 | if len(self.spec): | 38 | return self._as_thread_revision_id(branch, state, threads) |
1149 | 54 | index = state.thread_index(self.spec) | ||
1150 | 55 | else: | ||
1151 | 56 | current_thread = branch.nick | ||
1152 | 57 | index = state.thread_index(current_thread) - 1 | ||
1153 | 58 | if index < 0: | ||
1154 | 59 | raise NoLowerThread() | ||
1155 | 60 | return threads[index][1] | ||
1157 | 61 | finally: | 39 | finally: |
1158 | 62 | branch.unlock() | 40 | branch.unlock() |
1159 | 63 | 41 | ||
1160 | 64 | 42 | ||
1166 | 65 | revspec_registry = getattr(revisionspec, 'revspec_registry', None) | 43 | class RevisionSpecBelow(LoomRevisionSpec): |
1167 | 66 | if revspec_registry is not None: | 44 | """The below: revision specifier.""" |
1168 | 67 | revspec_registry.register('thread:', RevisionSpecThread) | 45 | |
1169 | 68 | else: | 46 | help_txt = """Selects the tip of the thread below a thread from a loom. |
1170 | 69 | revisionspec.SPEC_TYPES.append(RevisionSpecThread) | 47 | |
1171 | 48 | Selects the tip of the thread below a thread in a loom. | ||
1172 | 49 | |||
1173 | 50 | Examples:: | ||
1174 | 51 | |||
1175 | 52 | below: -> return the tip of the next lower thread. | ||
1176 | 53 | below:foo -> return the tip of the thread under the one | ||
1177 | 54 | named 'foo' | ||
1178 | 55 | |||
1179 | 56 | see also: loom, the thread: revision specifier | ||
1180 | 57 | """ | ||
1181 | 58 | |||
1182 | 59 | prefix = 'below:' | ||
1183 | 60 | |||
1184 | 61 | def _as_thread_revision_id(self, branch, state, threads): | ||
1185 | 62 | # '' -> next lower | ||
1186 | 63 | # foo -> thread under foo | ||
1187 | 64 | if len(self.spec): | ||
1188 | 65 | index = state.thread_index(self.spec) | ||
1189 | 66 | else: | ||
1190 | 67 | current_thread = branch.nick | ||
1191 | 68 | index = state.thread_index(current_thread) | ||
1192 | 69 | if index < 1: | ||
1193 | 70 | raise NoLowerThread() | ||
1194 | 71 | return threads[index - 1][1] | ||
1195 | 72 | |||
1196 | 73 | |||
1197 | 74 | class RevisionSpecThread(LoomRevisionSpec): | ||
1198 | 75 | """The thread: revision specifier.""" | ||
1199 | 76 | |||
1200 | 77 | help_txt = """Selects the tip of a thread from a loom. | ||
1201 | 78 | |||
1202 | 79 | Selects the tip of a thread in a loom. | ||
1203 | 80 | |||
1204 | 81 | Examples:: | ||
1205 | 82 | |||
1206 | 83 | thread: -> return the tip of the next lower thread. | ||
1207 | 84 | thread:foo -> return the tip of the thread named 'foo' | ||
1208 | 85 | |||
1209 | 86 | see also: loom, the below: revision specifier | ||
1210 | 87 | """ | ||
1211 | 88 | |||
1212 | 89 | prefix = 'thread:' | ||
1213 | 90 | |||
1214 | 91 | def _as_thread_revision_id(self, branch, state, threads): | ||
1215 | 92 | # '' -> next lower | ||
1216 | 93 | # foo -> named | ||
1217 | 94 | if len(self.spec): | ||
1218 | 95 | index = state.thread_index(self.spec) | ||
1219 | 96 | else: | ||
1220 | 97 | current_thread = branch.nick | ||
1221 | 98 | index = state.thread_index(current_thread) - 1 | ||
1222 | 99 | if index < 0: | ||
1223 | 100 | raise NoLowerThread() | ||
1224 | 101 | return threads[index][1] | ||
1225 | 102 | |||
1226 | 103 | |||
1227 | 104 | |||
1228 | 70 | 105 | ||
1229 | === modified file 'setup.py' | |||
1230 | --- setup.py 2008-05-30 02:39:07 +0000 | |||
1231 | +++ setup.py 2010-08-05 18:57:44 +0000 | |||
1232 | @@ -20,13 +20,11 @@ | |||
1233 | 20 | "Bazaar-NG Loom branch format 6\n":"Loom branch format 6", | 20 | "Bazaar-NG Loom branch format 6\n":"Loom branch format 6", |
1234 | 21 | } | 21 | } |
1235 | 22 | 22 | ||
1239 | 23 | bzr_plugin_version = (1, 4, 0, 'dev', 0) | 23 | from version import * |
1237 | 24 | bzr_minimum_version = (1, 0, 0) | ||
1238 | 25 | bzr_maximum_version = None | ||
1240 | 26 | 24 | ||
1242 | 27 | if __name__ == 'main': | 25 | if __name__ == '__main__': |
1243 | 28 | setup(name="Loom", | 26 | setup(name="Loom", |
1245 | 29 | version="1.4.0dev0", | 27 | version="2.2.1dev0", |
1246 | 30 | description="Loom plugin for bzr.", | 28 | description="Loom plugin for bzr.", |
1247 | 31 | author="Canonical Ltd", | 29 | author="Canonical Ltd", |
1248 | 32 | author_email="bazaar@lists.canonical.com", | 30 | author_email="bazaar@lists.canonical.com", |
1249 | 33 | 31 | ||
1250 | === modified file 'tests/__init__.py' | |||
1251 | --- tests/__init__.py 2008-09-12 01:55:17 +0000 | |||
1252 | +++ tests/__init__.py 2010-08-05 18:57:44 +0000 | |||
1253 | @@ -22,6 +22,7 @@ | |||
1254 | 22 | import bzrlib.plugins.loom.branch | 22 | import bzrlib.plugins.loom.branch |
1255 | 23 | from bzrlib.tests import TestCaseWithTransport | 23 | from bzrlib.tests import TestCaseWithTransport |
1256 | 24 | from bzrlib.tests.TestUtil import TestLoader, TestSuite | 24 | from bzrlib.tests.TestUtil import TestLoader, TestSuite |
1257 | 25 | from bzrlib.workingtree import WorkingTree | ||
1258 | 25 | 26 | ||
1259 | 26 | 27 | ||
1260 | 27 | def test_suite(): | 28 | def test_suite(): |
1261 | @@ -41,7 +42,9 @@ | |||
1262 | 41 | 42 | ||
1263 | 42 | def get_tree_with_loom(self, path="."): | 43 | def get_tree_with_loom(self, path="."): |
1264 | 43 | """Get a tree with no commits in loom format.""" | 44 | """Get a tree with no commits in loom format.""" |
1266 | 44 | tree = self.make_branch_and_tree(path) | 45 | # May open on Remote - we want the vfs backed version for loom tests. |
1267 | 46 | self.make_branch_and_tree(path) | ||
1268 | 47 | tree = WorkingTree.open(path) | ||
1269 | 45 | bzrlib.plugins.loom.branch.loomify(tree.branch) | 48 | bzrlib.plugins.loom.branch.loomify(tree.branch) |
1270 | 46 | return tree.bzrdir.open_workingtree() | 49 | return tree.bzrdir.open_workingtree() |
1271 | 47 | 50 | ||
1272 | 48 | 51 | ||
1273 | === modified file 'tests/blackbox.py' | |||
1274 | --- tests/blackbox.py 2008-11-24 16:38:16 +0000 | |||
1275 | +++ tests/blackbox.py 2010-08-05 18:57:44 +0000 | |||
1276 | @@ -35,7 +35,7 @@ | |||
1277 | 35 | def _add_patch(self, tree, name): | 35 | def _add_patch(self, tree, name): |
1278 | 36 | """Add a patch to a new thread, returning the revid of te commit.""" | 36 | """Add a patch to a new thread, returning the revid of te commit.""" |
1279 | 37 | tree.branch.new_thread(name) | 37 | tree.branch.new_thread(name) |
1281 | 38 | tree.branch.nick = name | 38 | tree.branch._set_nick(name) |
1282 | 39 | self.build_tree([name]) | 39 | self.build_tree([name]) |
1283 | 40 | tree.add(name) | 40 | tree.add(name) |
1284 | 41 | return tree.commit(name) | 41 | return tree.commit(name) |
1285 | @@ -147,11 +147,11 @@ | |||
1286 | 147 | self.assertShowLoom(['vendor'], 'vendor') | 147 | self.assertShowLoom(['vendor'], 'vendor') |
1287 | 148 | tree.branch.new_thread('debian') | 148 | tree.branch.new_thread('debian') |
1288 | 149 | self.assertShowLoom(['vendor', 'debian'], 'vendor') | 149 | self.assertShowLoom(['vendor', 'debian'], 'vendor') |
1290 | 150 | tree.branch.nick = 'debian' | 150 | tree.branch._set_nick('debian') |
1291 | 151 | self.assertShowLoom(['vendor', 'debian'], 'debian') | 151 | self.assertShowLoom(['vendor', 'debian'], 'debian') |
1292 | 152 | tree.branch.new_thread('patch A', 'vendor') | 152 | tree.branch.new_thread('patch A', 'vendor') |
1293 | 153 | self.assertShowLoom(['vendor', 'patch A', 'debian'], 'debian') | 153 | self.assertShowLoom(['vendor', 'patch A', 'debian'], 'debian') |
1295 | 154 | tree.branch.nick = 'patch A' | 154 | tree.branch._set_nick('patch A') |
1296 | 155 | self.assertShowLoom(['vendor', 'patch A', 'debian'], 'patch A') | 155 | self.assertShowLoom(['vendor', 'patch A', 'debian'], 'patch A') |
1297 | 156 | 156 | ||
1298 | 157 | def test_show_loom_with_location(self): | 157 | def test_show_loom_with_location(self): |
1299 | @@ -266,6 +266,17 @@ | |||
1300 | 266 | "All changes applied successfully.\nMoved to thread 'thread2'.\n", | 266 | "All changes applied successfully.\nMoved to thread 'thread2'.\n", |
1301 | 267 | err) | 267 | err) |
1302 | 268 | 268 | ||
1303 | 269 | def test_switch_dash_b(self): | ||
1304 | 270 | # 'bzr switch -b new-thread' makes and switches to a new thread. | ||
1305 | 271 | tree = self.get_vendor_loom() | ||
1306 | 272 | self._add_patch(tree, 'thread2') | ||
1307 | 273 | LoomTreeDecorator(tree).down_thread('vendor') | ||
1308 | 274 | self.assertEqual(tree.branch.nick, 'vendor') | ||
1309 | 275 | out, err = self.run_bzr(['switch', '-b', 'thread1'], retcode=0) | ||
1310 | 276 | self.assertEqual(tree.branch.nick, 'thread1') | ||
1311 | 277 | self.assertEqual('', out) | ||
1312 | 278 | self.assertEqual('', err) | ||
1313 | 279 | |||
1314 | 269 | 280 | ||
1315 | 270 | class TestRecord(TestsWithLooms): | 281 | class TestRecord(TestsWithLooms): |
1316 | 271 | 282 | ||
1317 | @@ -281,7 +292,7 @@ | |||
1318 | 281 | """Adding a new thread is enough to allow recording.""" | 292 | """Adding a new thread is enough to allow recording.""" |
1319 | 282 | tree = self.get_vendor_loom() | 293 | tree = self.get_vendor_loom() |
1320 | 283 | tree.branch.new_thread('feature') | 294 | tree.branch.new_thread('feature') |
1322 | 284 | tree.branch.nick = 'feature' | 295 | tree.branch._set_nick('feature') |
1323 | 285 | out, err = self.run_bzr(['record', 'add feature branch.']) | 296 | out, err = self.run_bzr(['record', 'add feature branch.']) |
1324 | 286 | self.assertEqual('Loom recorded.\n', out) | 297 | self.assertEqual('Loom recorded.\n', out) |
1325 | 287 | self.assertEqual('', err) | 298 | self.assertEqual('', err) |
1326 | @@ -303,7 +314,7 @@ | |||
1327 | 303 | """moving down when the revision is unchanged should work.""" | 314 | """moving down when the revision is unchanged should work.""" |
1328 | 304 | tree = self.get_vendor_loom() | 315 | tree = self.get_vendor_loom() |
1329 | 305 | tree.branch.new_thread('patch') | 316 | tree.branch.new_thread('patch') |
1331 | 306 | tree.branch.nick = 'patch' | 317 | tree.branch._set_nick('patch') |
1332 | 307 | rev = tree.last_revision() | 318 | rev = tree.last_revision() |
1333 | 308 | out, err = self.run_bzr(['down-thread']) | 319 | out, err = self.run_bzr(['down-thread']) |
1334 | 309 | self.assertEqual('', out) | 320 | self.assertEqual('', out) |
1335 | @@ -314,7 +325,7 @@ | |||
1336 | 314 | def test_down_thread_removes_changes_between_threads(self): | 325 | def test_down_thread_removes_changes_between_threads(self): |
1337 | 315 | tree = self.get_vendor_loom() | 326 | tree = self.get_vendor_loom() |
1338 | 316 | tree.branch.new_thread('patch') | 327 | tree.branch.new_thread('patch') |
1340 | 317 | tree.branch.nick = 'patch' | 328 | tree.branch._set_nick('patch') |
1341 | 318 | rev = tree.last_revision() | 329 | rev = tree.last_revision() |
1342 | 319 | self.build_tree(['afile']) | 330 | self.build_tree(['afile']) |
1343 | 320 | tree.add('afile') | 331 | tree.add('afile') |
1344 | @@ -336,7 +347,7 @@ | |||
1345 | 336 | """Do a down thread when the lower patch is not in the r-h of the old.""" | 347 | """Do a down thread when the lower patch is not in the r-h of the old.""" |
1346 | 337 | tree = self.get_vendor_loom() | 348 | tree = self.get_vendor_loom() |
1347 | 338 | tree.branch.new_thread('patch') | 349 | tree.branch.new_thread('patch') |
1349 | 339 | tree.branch.nick = 'vendor' | 350 | tree.branch._set_nick('vendor') |
1350 | 340 | # do a null change in vendor - a new release. | 351 | # do a null change in vendor - a new release. |
1351 | 341 | vendor_release = tree.commit('new vendor release.', allow_pointless=True) | 352 | vendor_release = tree.commit('new vendor release.', allow_pointless=True) |
1352 | 342 | # pop up, then down | 353 | # pop up, then down |
1353 | @@ -389,7 +400,7 @@ | |||
1354 | 389 | """Trying to down-thread with changes causes an error.""" | 400 | """Trying to down-thread with changes causes an error.""" |
1355 | 390 | tree = self.get_vendor_loom() | 401 | tree = self.get_vendor_loom() |
1356 | 391 | tree.branch.new_thread('upper-thread') | 402 | tree.branch.new_thread('upper-thread') |
1358 | 392 | tree.branch.nick = 'upper-thread' | 403 | tree.branch._set_nick('upper-thread') |
1359 | 393 | self.build_tree(['new-file']) | 404 | self.build_tree(['new-file']) |
1360 | 394 | tree.add('new-file') | 405 | tree.add('new-file') |
1361 | 395 | out, err = self.run_bzr('down-thread', retcode=3) | 406 | out, err = self.run_bzr('down-thread', retcode=3) |
1362 | @@ -405,33 +416,35 @@ | |||
1363 | 405 | self.assertEqual('', out) | 416 | self.assertEqual('', out) |
1364 | 406 | self.assertEqual( | 417 | self.assertEqual( |
1365 | 407 | 'bzr: ERROR: Cannot move up from the highest thread.\n', err) | 418 | 'bzr: ERROR: Cannot move up from the highest thread.\n', err) |
1367 | 408 | 419 | ||
1368 | 409 | def test_up_thread_same_revision(self): | 420 | def test_up_thread_same_revision(self): |
1369 | 410 | """moving up when the revision is unchanged should work.""" | 421 | """moving up when the revision is unchanged should work.""" |
1370 | 411 | tree = self.get_vendor_loom() | 422 | tree = self.get_vendor_loom() |
1371 | 412 | tree.branch.new_thread('patch') | 423 | tree.branch.new_thread('patch') |
1373 | 413 | tree.branch.nick = 'vendor' | 424 | tree.branch._set_nick('vendor') |
1374 | 414 | rev = tree.last_revision() | 425 | rev = tree.last_revision() |
1375 | 415 | out, err = self.run_bzr(['up-thread']) | 426 | out, err = self.run_bzr(['up-thread']) |
1376 | 416 | self.assertEqual('', out) | 427 | self.assertEqual('', out) |
1377 | 417 | self.assertEqual('', err) | 428 | self.assertEqual('', err) |
1378 | 418 | self.assertEqual('patch', tree.branch.nick) | 429 | self.assertEqual('patch', tree.branch.nick) |
1379 | 419 | self.assertEqual(rev, tree.last_revision()) | 430 | self.assertEqual(rev, tree.last_revision()) |
1382 | 420 | 431 | ||
1383 | 421 | def test_up_thread_preserves_changes(self): | 432 | def test_up_thread_manual_preserves_changes(self): |
1384 | 422 | tree = self.get_vendor_loom() | 433 | tree = self.get_vendor_loom() |
1385 | 423 | tree.branch.new_thread('patch') | 434 | tree.branch.new_thread('patch') |
1387 | 424 | tree.branch.nick = 'vendor' | 435 | tree.branch._set_nick('vendor') |
1388 | 425 | patch_rev = tree.last_revision() | 436 | patch_rev = tree.last_revision() |
1389 | 426 | # add a change in vendor - a new release. | 437 | # add a change in vendor - a new release. |
1390 | 427 | self.build_tree(['afile']) | 438 | self.build_tree(['afile']) |
1391 | 428 | tree.add('afile') | 439 | tree.add('afile') |
1392 | 429 | vendor_release = tree.commit('new vendor release adds a file.') | 440 | vendor_release = tree.commit('new vendor release adds a file.') |
1394 | 430 | out, err = self.run_bzr(['up-thread']) | 441 | out, err = self.run_bzr(['up-thread', '--manual']) |
1395 | 431 | self.assertEqual('', out) | 442 | self.assertEqual('', out) |
1396 | 432 | self.assertEqual( | 443 | self.assertEqual( |
1397 | 433 | "All changes applied successfully.\n" | 444 | "All changes applied successfully.\n" |
1399 | 434 | "Moved to thread 'patch'.\n", err) | 445 | "Moved to thread 'patch'.\n" |
1400 | 446 | 'This thread is now empty, you may wish to run "bzr ' | ||
1401 | 447 | 'combine-thread" to remove it.\n', err) | ||
1402 | 435 | self.assertEqual('patch', tree.branch.nick) | 448 | self.assertEqual('patch', tree.branch.nick) |
1403 | 436 | # the tree needs to be updated. | 449 | # the tree needs to be updated. |
1404 | 437 | self.assertEqual(patch_rev, tree.last_revision()) | 450 | self.assertEqual(patch_rev, tree.last_revision()) |
1405 | @@ -442,11 +455,18 @@ | |||
1406 | 442 | self.run_bzr(['diff'], retcode=1) | 455 | self.run_bzr(['diff'], retcode=1) |
1407 | 443 | self.assertEqual([patch_rev, vendor_release], tree.get_parent_ids()) | 456 | self.assertEqual([patch_rev, vendor_release], tree.get_parent_ids()) |
1408 | 444 | 457 | ||
1409 | 458 | def test_up_thread_manual_rejects_specified_thread(self): | ||
1410 | 459 | tree = self.get_vendor_loom() | ||
1411 | 460 | tree.branch.new_thread('patch') | ||
1412 | 461 | out, err = self.run_bzr('up-thread --manual patch', retcode=3) | ||
1413 | 462 | self.assertContainsRe(err, 'Specifying a thread does not work with' | ||
1414 | 463 | ' --manual.') | ||
1415 | 464 | |||
1416 | 445 | def test_up_thread_gets_conflicts(self): | 465 | def test_up_thread_gets_conflicts(self): |
1417 | 446 | """Do a change in both the baseline and the next patch up.""" | 466 | """Do a change in both the baseline and the next patch up.""" |
1418 | 447 | tree = self.get_vendor_loom() | 467 | tree = self.get_vendor_loom() |
1419 | 448 | tree.branch.new_thread('patch') | 468 | tree.branch.new_thread('patch') |
1421 | 449 | tree.branch.nick = 'patch' | 469 | tree.branch._set_nick('patch') |
1422 | 450 | # add a change in patch - a new release. | 470 | # add a change in patch - a new release. |
1423 | 451 | self.build_tree(['afile']) | 471 | self.build_tree(['afile']) |
1424 | 452 | tree.add('afile') | 472 | tree.add('afile') |
1425 | @@ -483,14 +503,72 @@ | |||
1426 | 483 | self.run_bzr(['down-thread']) | 503 | self.run_bzr(['down-thread']) |
1427 | 484 | self.run_bzr(['up-thread', '--lca']) | 504 | self.run_bzr(['up-thread', '--lca']) |
1428 | 485 | 505 | ||
1430 | 486 | def test_up_thread_auto(self): | 506 | def test_up_thread_no_manual(self): |
1431 | 487 | tree = self.get_vendor_loom() | 507 | tree = self.get_vendor_loom() |
1432 | 488 | tree.branch.new_thread('middle') | 508 | tree.branch.new_thread('middle') |
1433 | 489 | tree.branch.new_thread('top') | 509 | tree.branch.new_thread('top') |
1435 | 490 | self.run_bzr('up-thread --auto') | 510 | self.run_bzr('up-thread') |
1436 | 491 | branch = _mod_branch.Branch.open('.') | 511 | branch = _mod_branch.Branch.open('.') |
1437 | 492 | self.assertEqual('top', branch.nick) | 512 | self.assertEqual('top', branch.nick) |
1438 | 493 | 513 | ||
1439 | 514 | def test_up_with_clean_merge_leaving_thread_empty(self): | ||
1440 | 515 | """This tests what happens when a thread becomes empty. | ||
1441 | 516 | |||
1442 | 517 | A thread becomes empty when all its changes are included in | ||
1443 | 518 | a lower thread, and so its diff to the thread below contains | ||
1444 | 519 | nothing. | ||
1445 | 520 | |||
1446 | 521 | The user should be warned when this happens. | ||
1447 | 522 | """ | ||
1448 | 523 | tree = self.get_vendor_loom() | ||
1449 | 524 | self.build_tree(['afile']) | ||
1450 | 525 | tree.add('afile') | ||
1451 | 526 | patch_rev = tree.commit('add afile in base') | ||
1452 | 527 | tree.branch.new_thread('patch') | ||
1453 | 528 | tree.branch._set_nick('patch') | ||
1454 | 529 | # make a change to afile in patch. | ||
1455 | 530 | f = open('afile', 'wb') | ||
1456 | 531 | try: | ||
1457 | 532 | f.write('new contents of afile\n') | ||
1458 | 533 | finally: | ||
1459 | 534 | f.close() | ||
1460 | 535 | patch_rev = tree.commit('make a change to afile') | ||
1461 | 536 | # make the same change in vendor. | ||
1462 | 537 | self.run_bzr(['down-thread']) | ||
1463 | 538 | f = open('afile', 'wb') | ||
1464 | 539 | try: | ||
1465 | 540 | f.write('new contents of afile\n') | ||
1466 | 541 | finally: | ||
1467 | 542 | f.close() | ||
1468 | 543 | vendor_release = tree.commit('make the same change to afile') | ||
1469 | 544 | # check that the trees no longer differ after the up merge, | ||
1470 | 545 | # and that we are | ||
1471 | 546 | out, err = self.run_bzr(['up-thread', '--manual']) | ||
1472 | 547 | self.assertEqual('', out) | ||
1473 | 548 | self.assertStartsWith(err, | ||
1474 | 549 | "All changes applied successfully.\n" | ||
1475 | 550 | "Moved to thread 'patch'.\n" | ||
1476 | 551 | 'This thread is now empty, you may wish to run "bzr ' | ||
1477 | 552 | 'combine-thread" to remove it.\n') | ||
1478 | 553 | self.assertEqual('patch', tree.branch.nick) | ||
1479 | 554 | # the tree needs to be updated. | ||
1480 | 555 | self.assertEqual(patch_rev, tree.last_revision()) | ||
1481 | 556 | # the branch needs to be updated. | ||
1482 | 557 | self.assertEqual(patch_rev, tree.branch.last_revision()) | ||
1483 | 558 | self.assertTrue(tree.has_filename('afile')) | ||
1484 | 559 | # diff should return 0 now as we have no uncommitted changes. | ||
1485 | 560 | self.run_bzr(['diff']) | ||
1486 | 561 | self.assertEqual([patch_rev, vendor_release], tree.get_parent_ids()) | ||
1487 | 562 | |||
1488 | 563 | def test_up_thread_accepts_thread(self): | ||
1489 | 564 | tree = self.get_vendor_loom() | ||
1490 | 565 | tree.branch.new_thread('lower-middle') | ||
1491 | 566 | tree.branch.new_thread('upper-middle') | ||
1492 | 567 | tree.branch.new_thread('top') | ||
1493 | 568 | self.run_bzr('up-thread upper-middle') | ||
1494 | 569 | branch = _mod_branch.Branch.open('.') | ||
1495 | 570 | self.assertEqual('upper-middle', branch.nick) | ||
1496 | 571 | |||
1497 | 494 | 572 | ||
1498 | 495 | class TestPush(TestsWithLooms): | 573 | class TestPush(TestsWithLooms): |
1499 | 496 | 574 | ||
1500 | @@ -592,7 +670,7 @@ | |||
1501 | 592 | # not in, so we can revert that by name, | 670 | # not in, so we can revert that by name, |
1502 | 593 | tree = self.get_vendor_loom() | 671 | tree = self.get_vendor_loom() |
1503 | 594 | tree.branch.new_thread('after-vendor') | 672 | tree.branch.new_thread('after-vendor') |
1505 | 595 | tree.branch.nick = 'after-vendor' | 673 | tree.branch._set_nick('after-vendor') |
1506 | 596 | tree.commit('after-vendor commit', allow_pointless=True) | 674 | tree.commit('after-vendor commit', allow_pointless=True) |
1507 | 597 | tree.branch.record_loom('save loom with vendor and after-vendor') | 675 | tree.branch.record_loom('save loom with vendor and after-vendor') |
1508 | 598 | old_threads = tree.branch.get_loom_state().get_threads() | 676 | old_threads = tree.branch.get_loom_state().get_threads() |
1509 | @@ -632,6 +710,56 @@ | |||
1510 | 632 | loom_tree.down_thread() | 710 | loom_tree.down_thread() |
1511 | 633 | return tree, loom_tree | 711 | return tree, loom_tree |
1512 | 634 | 712 | ||
1513 | 713 | def get_loom_with_unique_thread(self): | ||
1514 | 714 | """Return a loom with a unique thread. | ||
1515 | 715 | |||
1516 | 716 | That is: | ||
1517 | 717 | vendor:[] | ||
1518 | 718 | unique-thread:[vendor] | ||
1519 | 719 | above-vendor:[vendor] | ||
1520 | 720 | |||
1521 | 721 | - unique-thread has work not in vendor and not in above-vendor. | ||
1522 | 722 | |||
1523 | 723 | The returned loom is on the vendor thread. | ||
1524 | 724 | """ | ||
1525 | 725 | tree, _ = self.get_two_thread_loom() | ||
1526 | 726 | tree.branch.new_thread('unique-thread', 'vendor') | ||
1527 | 727 | loom_tree = LoomTreeDecorator(tree) | ||
1528 | 728 | loom_tree.up_thread() | ||
1529 | 729 | self.build_tree(['file-b']) | ||
1530 | 730 | tree.add('file-b') | ||
1531 | 731 | tree.commit('a unique change', rev_id='uniquely-yrs-1') | ||
1532 | 732 | loom_tree.down_thread() | ||
1533 | 733 | return tree, loom_tree | ||
1534 | 734 | |||
1535 | 735 | def test_combine_unmerged_thread_force(self): | ||
1536 | 736 | """Combining a thread with unique work works with --force.""" | ||
1537 | 737 | tree, loom_tree = self.get_loom_with_unique_thread() | ||
1538 | 738 | vendor_revid = tree.last_revision() | ||
1539 | 739 | loom_tree.up_thread() | ||
1540 | 740 | out, err = self.run_bzr(['combine-thread', '--force']) | ||
1541 | 741 | self.assertEqual('', out) | ||
1542 | 742 | self.assertEqual( | ||
1543 | 743 | "Combining thread 'unique-thread' into 'vendor'\n" | ||
1544 | 744 | 'All changes applied successfully.\n' | ||
1545 | 745 | "Moved to thread 'vendor'.\n", | ||
1546 | 746 | err) | ||
1547 | 747 | self.assertEqual(vendor_revid, tree.last_revision()) | ||
1548 | 748 | self.assertEqual('vendor', tree.branch.nick) | ||
1549 | 749 | |||
1550 | 750 | def test_combine_unmerged_thread_errors(self): | ||
1551 | 751 | """Combining a thread with unique work errors without --force.""" | ||
1552 | 752 | tree, loom_tree = self.get_loom_with_unique_thread() | ||
1553 | 753 | loom_tree.up_thread() | ||
1554 | 754 | unique_revid = tree.last_revision() | ||
1555 | 755 | out, err = self.run_bzr(['combine-thread'], retcode=3) | ||
1556 | 756 | self.assertEqual('', out) | ||
1557 | 757 | self.assertEqual("bzr: ERROR: " | ||
1558 | 758 | "Thread 'unique-thread' has unmerged work. Use --force to combine anyway.\n", | ||
1559 | 759 | err) | ||
1560 | 760 | self.assertEqual(unique_revid, tree.last_revision()) | ||
1561 | 761 | self.assertEqual('unique-thread', tree.branch.nick) | ||
1562 | 762 | |||
1563 | 635 | def test_combine_last_two_threads(self): | 763 | def test_combine_last_two_threads(self): |
1564 | 636 | """Doing a combine on two threads gives you just the bottom one.""" | 764 | """Doing a combine on two threads gives you just the bottom one.""" |
1565 | 637 | tree, loom_tree = self.get_two_thread_loom() | 765 | tree, loom_tree = self.get_two_thread_loom() |
1566 | 638 | 766 | ||
1567 | === modified file 'tests/test_branch.py' | |||
1568 | --- tests/test_branch.py 2008-11-24 16:38:16 +0000 | |||
1569 | +++ tests/test_branch.py 2010-08-05 18:57:44 +0000 | |||
1570 | @@ -23,6 +23,7 @@ | |||
1571 | 23 | from bzrlib.branch import Branch | 23 | from bzrlib.branch import Branch |
1572 | 24 | import bzrlib.errors as errors | 24 | import bzrlib.errors as errors |
1573 | 25 | from bzrlib.plugins.loom.branch import ( | 25 | from bzrlib.plugins.loom.branch import ( |
1574 | 26 | AlreadyLoom, | ||
1575 | 26 | EMPTY_REVISION, | 27 | EMPTY_REVISION, |
1576 | 27 | loomify, | 28 | loomify, |
1577 | 28 | require_loom_branch, | 29 | require_loom_branch, |
1578 | @@ -33,7 +34,10 @@ | |||
1579 | 33 | from bzrlib.plugins.loom.tree import LoomTreeDecorator | 34 | from bzrlib.plugins.loom.tree import LoomTreeDecorator |
1580 | 34 | import bzrlib.revision | 35 | import bzrlib.revision |
1581 | 35 | from bzrlib.revision import NULL_REVISION | 36 | from bzrlib.revision import NULL_REVISION |
1583 | 36 | from bzrlib.tests import TestCaseWithTransport | 37 | from bzrlib.tests import ( |
1584 | 38 | TestCaseWithTransport, | ||
1585 | 39 | test_server, | ||
1586 | 40 | ) | ||
1587 | 37 | from bzrlib.transport import get_transport | 41 | from bzrlib.transport import get_transport |
1588 | 38 | from bzrlib.workingtree import WorkingTree | 42 | from bzrlib.workingtree import WorkingTree |
1589 | 39 | 43 | ||
1590 | @@ -48,11 +52,18 @@ | |||
1591 | 48 | self.assertFileEqual('Loom current 1\n\n', '.bzr/branch/last-loom') | 52 | self.assertFileEqual('Loom current 1\n\n', '.bzr/branch/last-loom') |
1592 | 49 | 53 | ||
1593 | 50 | 54 | ||
1594 | 55 | |||
1595 | 56 | class StubFormat(object): | ||
1596 | 57 | |||
1597 | 58 | def network_name(self): | ||
1598 | 59 | return "Nothing to see." | ||
1599 | 60 | |||
1600 | 61 | |||
1601 | 51 | class LockableStub(object): | 62 | class LockableStub(object): |
1602 | 52 | 63 | ||
1603 | 53 | def __init__(self): | 64 | def __init__(self): |
1604 | 54 | self._calls = [] | 65 | self._calls = [] |
1606 | 55 | self._format = "Nothing to see." | 66 | self._format = StubFormat() |
1607 | 56 | 67 | ||
1608 | 57 | def lock_write(self): | 68 | def lock_write(self): |
1609 | 58 | self._calls.append(("write",)) | 69 | self._calls.append(("write",)) |
1610 | @@ -67,8 +78,7 @@ | |||
1611 | 67 | branch = self.make_branch('.') | 78 | branch = self.make_branch('.') |
1612 | 68 | self.assertRaises(NotALoom, require_loom_branch, branch) | 79 | self.assertRaises(NotALoom, require_loom_branch, branch) |
1613 | 69 | 80 | ||
1616 | 70 | 81 | def works_on_format(self, format): | |
1615 | 71 | def works_on_loom(self, format): | ||
1617 | 72 | branch = self.make_branch('.', format) | 82 | branch = self.make_branch('.', format) |
1618 | 73 | loomify(branch) | 83 | loomify(branch) |
1619 | 74 | # reopen it | 84 | # reopen it |
1620 | @@ -76,13 +86,19 @@ | |||
1621 | 76 | self.assertEqual(None, require_loom_branch(branch)) | 86 | self.assertEqual(None, require_loom_branch(branch)) |
1622 | 77 | 87 | ||
1623 | 78 | def test_works_on_loom1(self): | 88 | def test_works_on_loom1(self): |
1625 | 79 | self.works_on_loom('knit') | 89 | self.works_on_format('knit') |
1626 | 80 | 90 | ||
1627 | 81 | def test_works_on_loom6(self): | 91 | def test_works_on_loom6(self): |
1629 | 82 | self.works_on_loom('pack-0.92') | 92 | self.works_on_format('pack-0.92') |
1630 | 83 | 93 | ||
1631 | 84 | def test_works_on_loom7(self): | 94 | def test_works_on_loom7(self): |
1633 | 85 | self.works_on_loom('1.6') | 95 | self.works_on_format('1.6') |
1634 | 96 | |||
1635 | 97 | def test_no_harm_to_looms(self): | ||
1636 | 98 | branch = self.make_branch('.') | ||
1637 | 99 | loomify(branch) | ||
1638 | 100 | branch = branch.bzrdir.open_branch() | ||
1639 | 101 | self.assertRaises(AlreadyLoom, loomify, branch) | ||
1640 | 86 | 102 | ||
1641 | 87 | 103 | ||
1642 | 88 | class TestLoomify(TestCaseWithTransport): | 104 | class TestLoomify(TestCaseWithTransport): |
1643 | @@ -132,6 +148,7 @@ | |||
1644 | 132 | bzrlib.plugins.loom.branch.LoomBranch7, | 148 | bzrlib.plugins.loom.branch.LoomBranch7, |
1645 | 133 | bzrlib.plugins.loom.branch.BzrBranchLoomFormat7) | 149 | bzrlib.plugins.loom.branch.BzrBranchLoomFormat7) |
1646 | 134 | 150 | ||
1647 | 151 | |||
1648 | 135 | class TestLoom(TestCaseWithLoom): | 152 | class TestLoom(TestCaseWithLoom): |
1649 | 136 | 153 | ||
1650 | 137 | def make_loom(self, path): | 154 | def make_loom(self, path): |
1651 | @@ -184,9 +201,9 @@ | |||
1652 | 184 | tree.branch.new_thread('baseline') | 201 | tree.branch.new_thread('baseline') |
1653 | 185 | tree.branch.new_thread('middlepoint') | 202 | tree.branch.new_thread('middlepoint') |
1654 | 186 | tree.branch.new_thread('endpoint') | 203 | tree.branch.new_thread('endpoint') |
1656 | 187 | tree.branch.nick = 'middlepoint' | 204 | tree.branch._set_nick('middlepoint') |
1657 | 188 | rev_id2 = tree.commit('middle', allow_pointless=True) | 205 | rev_id2 = tree.commit('middle', allow_pointless=True) |
1659 | 189 | tree.branch.nick = 'endpoint' | 206 | tree.branch._set_nick('endpoint') |
1660 | 190 | rev_id3 = tree.commit('end', allow_pointless=True) | 207 | rev_id3 = tree.commit('end', allow_pointless=True) |
1661 | 191 | tree.branch.new_thread('afterbase', 'baseline') | 208 | tree.branch.new_thread('afterbase', 'baseline') |
1662 | 192 | tree.branch.new_thread('aftermiddle', 'middlepoint') | 209 | tree.branch.new_thread('aftermiddle', 'middlepoint') |
1663 | @@ -209,7 +226,7 @@ | |||
1664 | 209 | tree = self.get_tree_with_one_commit() | 226 | tree = self.get_tree_with_one_commit() |
1665 | 210 | tree.branch.new_thread('baseline') | 227 | tree.branch.new_thread('baseline') |
1666 | 211 | tree.branch.new_thread('tail') | 228 | tree.branch.new_thread('tail') |
1668 | 212 | tree.branch.nick = 'baseline' | 229 | tree.branch._set_nick('baseline') |
1669 | 213 | first_rev = tree.last_revision() | 230 | first_rev = tree.last_revision() |
1670 | 214 | # lock the tree to prevent unlock triggering implicit record | 231 | # lock the tree to prevent unlock triggering implicit record |
1671 | 215 | tree.lock_write() | 232 | tree.lock_write() |
1672 | @@ -230,7 +247,7 @@ | |||
1673 | 230 | 247 | ||
1674 | 231 | def test_clone_empty_loom(self): | 248 | def test_clone_empty_loom(self): |
1675 | 232 | source_tree = self.get_tree_with_loom('source') | 249 | source_tree = self.get_tree_with_loom('source') |
1677 | 233 | source_tree.branch.nick = 'source' | 250 | source_tree.branch._set_nick('source') |
1678 | 234 | target_tree = source_tree.bzrdir.clone('target').open_workingtree() | 251 | target_tree = source_tree.bzrdir.clone('target').open_workingtree() |
1679 | 235 | self.assertLoomSproutedOk(source_tree, target_tree) | 252 | self.assertLoomSproutedOk(source_tree, target_tree) |
1680 | 236 | 253 | ||
1681 | @@ -244,7 +261,7 @@ | |||
1682 | 244 | source_tree = self.get_tree_with_one_commit('source') | 261 | source_tree = self.get_tree_with_one_commit('source') |
1683 | 245 | source_tree.branch.new_thread('bottom') | 262 | source_tree.branch.new_thread('bottom') |
1684 | 246 | source_tree.branch.new_thread('top') | 263 | source_tree.branch.new_thread('top') |
1686 | 247 | source_tree.branch.nick = 'top' | 264 | source_tree.branch._set_nick('top') |
1687 | 248 | source_tree.commit('phwoar', allow_pointless=True) | 265 | source_tree.commit('phwoar', allow_pointless=True) |
1688 | 249 | source_tree.branch.record_loom('commit to loom') | 266 | source_tree.branch.record_loom('commit to loom') |
1689 | 250 | target_tree = source_tree.bzrdir.clone('target').open_workingtree() | 267 | target_tree = source_tree.bzrdir.clone('target').open_workingtree() |
1690 | @@ -252,23 +269,33 @@ | |||
1691 | 252 | 269 | ||
1692 | 253 | def test_clone_nonempty_loom_bottom(self): | 270 | def test_clone_nonempty_loom_bottom(self): |
1693 | 254 | """Cloning loom should reset the current loom pointer.""" | 271 | """Cloning loom should reset the current loom pointer.""" |
1694 | 272 | self.make_and_clone_simple_loom() | ||
1695 | 273 | |||
1696 | 274 | def make_and_clone_simple_loom(self): | ||
1697 | 255 | source_tree = self.get_tree_with_one_commit('source') | 275 | source_tree = self.get_tree_with_one_commit('source') |
1698 | 256 | source_tree.branch.new_thread('bottom') | 276 | source_tree.branch.new_thread('bottom') |
1699 | 257 | source_tree.branch.new_thread('top') | 277 | source_tree.branch.new_thread('top') |
1701 | 258 | source_tree.branch.nick = 'top' | 278 | source_tree.branch._set_nick('top') |
1702 | 259 | source_tree.commit('phwoar', allow_pointless=True) | 279 | source_tree.commit('phwoar', allow_pointless=True) |
1703 | 260 | source_tree.branch.record_loom('commit to loom') | 280 | source_tree.branch.record_loom('commit to loom') |
1704 | 261 | LoomTreeDecorator(source_tree).down_thread() | 281 | LoomTreeDecorator(source_tree).down_thread() |
1707 | 262 | # now clone | 282 | # now clone from the 'default url' - transport_server rather than |
1708 | 263 | target_tree = source_tree.bzrdir.clone('target').open_workingtree() | 283 | # vfs_server. |
1709 | 284 | source_branch = Branch.open(self.get_url('source')) | ||
1710 | 285 | target_tree = source_branch.bzrdir.sprout('target').open_workingtree() | ||
1711 | 264 | self.assertLoomSproutedOk(source_tree, target_tree) | 286 | self.assertLoomSproutedOk(source_tree, target_tree) |
1712 | 265 | 287 | ||
1713 | 288 | def test_sprout_remote_loom(self): | ||
1714 | 289 | # RemoteBranch should permit sprouting properly. | ||
1715 | 290 | self.transport_server = test_server.SmartTCPServer_for_testing | ||
1716 | 291 | self.make_and_clone_simple_loom() | ||
1717 | 292 | |||
1718 | 266 | def test_sprout_nonempty_loom_bottom(self): | 293 | def test_sprout_nonempty_loom_bottom(self): |
1719 | 267 | """Sprouting always resets the loom to the top.""" | 294 | """Sprouting always resets the loom to the top.""" |
1720 | 268 | source_tree = self.get_tree_with_one_commit('source') | 295 | source_tree = self.get_tree_with_one_commit('source') |
1721 | 269 | source_tree.branch.new_thread('bottom') | 296 | source_tree.branch.new_thread('bottom') |
1722 | 270 | source_tree.branch.new_thread('top') | 297 | source_tree.branch.new_thread('top') |
1724 | 271 | source_tree.branch.nick = 'top' | 298 | source_tree.branch._set_nick('top') |
1725 | 272 | source_tree.commit('phwoar', allow_pointless=True) | 299 | source_tree.commit('phwoar', allow_pointless=True) |
1726 | 273 | source_tree.branch.record_loom('commit to loom') | 300 | source_tree.branch.record_loom('commit to loom') |
1727 | 274 | LoomTreeDecorator(source_tree).down_thread() | 301 | LoomTreeDecorator(source_tree).down_thread() |
1728 | @@ -322,10 +349,10 @@ | |||
1729 | 322 | source = self.get_tree_with_loom('source') | 349 | source = self.get_tree_with_loom('source') |
1730 | 323 | source.branch.new_thread('bottom') | 350 | source.branch.new_thread('bottom') |
1731 | 324 | source.branch.new_thread('top') | 351 | source.branch.new_thread('top') |
1733 | 325 | source.branch.nick = 'bottom' | 352 | source.branch._set_nick('bottom') |
1734 | 326 | source.branch.record_loom('commit to loom') | 353 | source.branch.record_loom('commit to loom') |
1735 | 327 | target = source.bzrdir.sprout('target').open_branch() | 354 | target = source.bzrdir.sprout('target').open_branch() |
1737 | 328 | target.nick = 'top' | 355 | target._set_nick('top') |
1738 | 329 | # put a commit in the bottom and top of this loom | 356 | # put a commit in the bottom and top of this loom |
1739 | 330 | bottom_rev1 = source.commit('commit my arse') | 357 | bottom_rev1 = source.commit('commit my arse') |
1740 | 331 | source_loom_tree = LoomTreeDecorator(source) | 358 | source_loom_tree = LoomTreeDecorator(source) |
1741 | @@ -355,14 +382,20 @@ | |||
1742 | 355 | 382 | ||
1743 | 356 | def test_pull_into_empty_loom(self): | 383 | def test_pull_into_empty_loom(self): |
1744 | 357 | """Doing a pull into a loom with no loom revisions works.""" | 384 | """Doing a pull into a loom with no loom revisions works.""" |
1745 | 385 | self.pull_into_empty_loom() | ||
1746 | 386 | |||
1747 | 387 | def pull_into_empty_loom(self): | ||
1748 | 358 | source = self.get_tree_with_loom('source') | 388 | source = self.get_tree_with_loom('source') |
1749 | 359 | target = source.bzrdir.sprout('target').open_branch() | 389 | target = source.bzrdir.sprout('target').open_branch() |
1750 | 360 | source.branch.new_thread('a thread') | 390 | source.branch.new_thread('a thread') |
1752 | 361 | source.branch.nick = 'a thread' | 391 | source.branch._set_nick('a thread') |
1753 | 362 | # put a commit in the thread for source. | 392 | # put a commit in the thread for source. |
1754 | 363 | bottom_rev1 = source.commit('commit a thread') | 393 | bottom_rev1 = source.commit('commit a thread') |
1755 | 364 | source.branch.record_loom('commit to loom') | 394 | source.branch.record_loom('commit to loom') |
1757 | 365 | target.pull(source.branch) | 395 | # now pull from the 'default url' - transport_server rather than |
1758 | 396 | # vfs_server - this may be a RemoteBranch. | ||
1759 | 397 | source_branch = Branch.open(self.get_url('source')) | ||
1760 | 398 | target.pull(source_branch) | ||
1761 | 366 | # check loom threads | 399 | # check loom threads |
1762 | 367 | threads = target.get_loom_state().get_threads() | 400 | threads = target.get_loom_state().get_threads() |
1763 | 368 | self.assertEqual( | 401 | self.assertEqual( |
1764 | @@ -374,12 +407,17 @@ | |||
1765 | 374 | self.assertTrue(target.repository.has_revision(rev_id)) | 407 | self.assertTrue(target.repository.has_revision(rev_id)) |
1766 | 375 | self.assertEqual(source.branch.loom_parents(), target.loom_parents()) | 408 | self.assertEqual(source.branch.loom_parents(), target.loom_parents()) |
1767 | 376 | 409 | ||
1768 | 410 | def test_pull_remote_loom(self): | ||
1769 | 411 | # RemoteBranch should permit sprouting properly. | ||
1770 | 412 | self.transport_server = test_server.SmartTCPServer_for_testing | ||
1771 | 413 | self.pull_into_empty_loom() | ||
1772 | 414 | |||
1773 | 377 | def test_pull_thread_at_null(self): | 415 | def test_pull_thread_at_null(self): |
1774 | 378 | """Doing a pull when the source loom has a thread with no history.""" | 416 | """Doing a pull when the source loom has a thread with no history.""" |
1775 | 379 | source = self.get_tree_with_loom('source') | 417 | source = self.get_tree_with_loom('source') |
1776 | 380 | target = source.bzrdir.sprout('target').open_branch() | 418 | target = source.bzrdir.sprout('target').open_branch() |
1777 | 381 | source.branch.new_thread('a thread') | 419 | source.branch.new_thread('a thread') |
1779 | 382 | source.branch.nick = 'a thread' | 420 | source.branch._set_nick('a thread') |
1780 | 383 | source.branch.record_loom('commit to loom') | 421 | source.branch.record_loom('commit to loom') |
1781 | 384 | target.pull(source.branch) | 422 | target.pull(source.branch) |
1782 | 385 | # check loom threads | 423 | # check loom threads |
1783 | @@ -398,10 +436,10 @@ | |||
1784 | 398 | source = self.get_tree_with_loom('source') | 436 | source = self.get_tree_with_loom('source') |
1785 | 399 | source.branch.new_thread('bottom') | 437 | source.branch.new_thread('bottom') |
1786 | 400 | source.branch.new_thread('top') | 438 | source.branch.new_thread('top') |
1788 | 401 | source.branch.nick = 'bottom' | 439 | source.branch._set_nick('bottom') |
1789 | 402 | source.branch.record_loom('commit to loom') | 440 | source.branch.record_loom('commit to loom') |
1790 | 403 | target = source.bzrdir.sprout('target').open_branch() | 441 | target = source.bzrdir.sprout('target').open_branch() |
1792 | 404 | target.nick = 'top' | 442 | target._set_nick('top') |
1793 | 405 | # put a commit in the bottom and top of this loom | 443 | # put a commit in the bottom and top of this loom |
1794 | 406 | bottom_rev1 = source.commit('commit bottom') | 444 | bottom_rev1 = source.commit('commit bottom') |
1795 | 407 | source_loom_tree = LoomTreeDecorator(source) | 445 | source_loom_tree = LoomTreeDecorator(source) |
1796 | @@ -432,7 +470,7 @@ | |||
1797 | 432 | def test_implicit_record(self): | 470 | def test_implicit_record(self): |
1798 | 433 | tree = self.get_tree_with_loom('source') | 471 | tree = self.get_tree_with_loom('source') |
1799 | 434 | tree.branch.new_thread('bottom') | 472 | tree.branch.new_thread('bottom') |
1801 | 435 | tree.branch.nick = 'bottom' | 473 | tree.branch._set_nick('bottom') |
1802 | 436 | tree.lock_write() | 474 | tree.lock_write() |
1803 | 437 | try: | 475 | try: |
1804 | 438 | bottom_rev1 = tree.commit('commit my arse') | 476 | bottom_rev1 = tree.commit('commit my arse') |
1805 | @@ -453,7 +491,7 @@ | |||
1806 | 453 | self.assertEqual([], tree.branch.loom_parents()) | 491 | self.assertEqual([], tree.branch.loom_parents()) |
1807 | 454 | # add a thread and record it. | 492 | # add a thread and record it. |
1808 | 455 | tree.branch.new_thread('bottom') | 493 | tree.branch.new_thread('bottom') |
1810 | 456 | tree.branch.nick = 'bottom' | 494 | tree.branch._set_nick('bottom') |
1811 | 457 | rev_id = tree.branch.record_loom('Setup test loom.') | 495 | rev_id = tree.branch.record_loom('Setup test loom.') |
1812 | 458 | # after recording, the parents list should have changed. | 496 | # after recording, the parents list should have changed. |
1813 | 459 | self.assertEqual([rev_id], tree.branch.loom_parents()) | 497 | self.assertEqual([rev_id], tree.branch.loom_parents()) |
1814 | @@ -464,7 +502,7 @@ | |||
1815 | 464 | # new threads | 502 | # new threads |
1816 | 465 | tree.branch.new_thread('foo') | 503 | tree.branch.new_thread('foo') |
1817 | 466 | tree.branch.new_thread('bar') | 504 | tree.branch.new_thread('bar') |
1819 | 467 | tree.branch.nick = 'bar' | 505 | tree.branch._set_nick('bar') |
1820 | 468 | last_rev = tree.branch.last_revision() | 506 | last_rev = tree.branch.last_revision() |
1821 | 469 | # and a change to the revision history of this thread | 507 | # and a change to the revision history of this thread |
1822 | 470 | tree.commit('change bar', allow_pointless=True) | 508 | tree.commit('change bar', allow_pointless=True) |
1823 | @@ -478,7 +516,7 @@ | |||
1824 | 478 | # new threads | 516 | # new threads |
1825 | 479 | tree.branch.new_thread('foo') | 517 | tree.branch.new_thread('foo') |
1826 | 480 | tree.branch.new_thread('bar') | 518 | tree.branch.new_thread('bar') |
1828 | 481 | tree.branch.nick = 'bar' | 519 | tree.branch._set_nick('bar') |
1829 | 482 | # and a change to the revision history of this thread | 520 | # and a change to the revision history of this thread |
1830 | 483 | tree.commit('change bar', allow_pointless=True) | 521 | tree.commit('change bar', allow_pointless=True) |
1831 | 484 | # now record | 522 | # now record |
1832 | @@ -503,7 +541,7 @@ | |||
1833 | 503 | # new threads | 541 | # new threads |
1834 | 504 | tree.branch.new_thread('base') | 542 | tree.branch.new_thread('base') |
1835 | 505 | tree.branch.new_thread('top') | 543 | tree.branch.new_thread('top') |
1837 | 506 | tree.branch.nick = 'top' | 544 | tree.branch._set_nick('top') |
1838 | 507 | # and a change to the revision history of this thread | 545 | # and a change to the revision history of this thread |
1839 | 508 | tree.tree.commit('change top', allow_pointless=True) | 546 | tree.tree.commit('change top', allow_pointless=True) |
1840 | 509 | last_rev = tree.branch.last_revision() | 547 | last_rev = tree.branch.last_revision() |
1841 | @@ -527,7 +565,7 @@ | |||
1842 | 527 | tree.branch.new_thread('foo') | 565 | tree.branch.new_thread('foo') |
1843 | 528 | tree.branch.new_thread('bar') | 566 | tree.branch.new_thread('bar') |
1844 | 529 | # do a commit, so the last_revision should change. | 567 | # do a commit, so the last_revision should change. |
1846 | 530 | tree.branch.nick = 'bar' | 568 | tree.branch._set_nick('bar') |
1847 | 531 | tree.commit('bar-ness', allow_pointless=True) | 569 | tree.commit('bar-ness', allow_pointless=True) |
1848 | 532 | tree.branch.revert_thread('bar') | 570 | tree.branch.revert_thread('bar') |
1849 | 533 | self.assertEqual( | 571 | self.assertEqual( |
1850 | @@ -540,11 +578,11 @@ | |||
1851 | 540 | # ensure we have some stuff to revert | 578 | # ensure we have some stuff to revert |
1852 | 541 | tree.branch.new_thread('foo') | 579 | tree.branch.new_thread('foo') |
1853 | 542 | tree.branch.new_thread('bar') | 580 | tree.branch.new_thread('bar') |
1855 | 543 | tree.branch.nick = 'foo' | 581 | tree.branch._set_nick('foo') |
1856 | 544 | # record the loom to put the threads in the basis | 582 | # record the loom to put the threads in the basis |
1857 | 545 | tree.branch.record_loom('record it!') | 583 | tree.branch.record_loom('record it!') |
1858 | 546 | # do a commit, so the last_revision should change. | 584 | # do a commit, so the last_revision should change. |
1860 | 547 | tree.branch.nick = 'bar' | 585 | tree.branch._set_nick('bar') |
1861 | 548 | tree.commit('bar-ness', allow_pointless=True) | 586 | tree.commit('bar-ness', allow_pointless=True) |
1862 | 549 | tree.branch.revert_thread('bar') | 587 | tree.branch.revert_thread('bar') |
1863 | 550 | self.assertEqual( | 588 | self.assertEqual( |
1864 | @@ -558,7 +596,7 @@ | |||
1865 | 558 | tree = self.get_tree_with_loom() | 596 | tree = self.get_tree_with_loom() |
1866 | 559 | tree.branch.new_thread('bar') | 597 | tree.branch.new_thread('bar') |
1867 | 560 | tree.branch.new_thread('foo') | 598 | tree.branch.new_thread('foo') |
1869 | 561 | tree.branch.nick = 'bar' | 599 | tree.branch._set_nick('bar') |
1870 | 562 | tree.branch.remove_thread('foo') | 600 | tree.branch.remove_thread('foo') |
1871 | 563 | state = tree.branch.get_loom_state() | 601 | state = tree.branch.get_loom_state() |
1872 | 564 | self.assertEqual([('bar', 'empty:', [])], state.get_threads()) | 602 | self.assertEqual([('bar', 'empty:', [])], state.get_threads()) |
1873 | @@ -569,17 +607,17 @@ | |||
1874 | 569 | self.assertEqual([], tree.branch.get_threads(NULL_REVISION)) | 607 | self.assertEqual([], tree.branch.get_threads(NULL_REVISION)) |
1875 | 570 | # and loom history should make no difference: | 608 | # and loom history should make no difference: |
1876 | 571 | tree.branch.new_thread('foo') | 609 | tree.branch.new_thread('foo') |
1878 | 572 | tree.branch.nick = 'foo' | 610 | tree.branch._set_nick('foo') |
1879 | 573 | tree.branch.record_loom('foo') | 611 | tree.branch.record_loom('foo') |
1880 | 574 | self.assertEqual([], tree.branch.get_threads(NULL_REVISION)) | 612 | self.assertEqual([], tree.branch.get_threads(NULL_REVISION)) |
1881 | 575 | 613 | ||
1882 | 576 | def get_multi_threaded(self): | 614 | def get_multi_threaded(self): |
1883 | 577 | tree = self.get_tree_with_loom() | 615 | tree = self.get_tree_with_loom() |
1884 | 578 | tree.branch.new_thread('thread1') | 616 | tree.branch.new_thread('thread1') |
1886 | 579 | tree.branch.nick = 'thread1' | 617 | tree.branch._set_nick('thread1') |
1887 | 580 | tree.commit('thread1', rev_id='thread1-id') | 618 | tree.commit('thread1', rev_id='thread1-id') |
1888 | 581 | tree.branch.new_thread('thread2', 'thread1') | 619 | tree.branch.new_thread('thread2', 'thread1') |
1890 | 582 | tree.branch.nick = 'thread2' | 620 | tree.branch._set_nick('thread2') |
1891 | 583 | tree.commit('thread2', rev_id='thread2-id') | 621 | tree.commit('thread2', rev_id='thread2-id') |
1892 | 584 | return tree | 622 | return tree |
1893 | 585 | 623 | ||
1894 | @@ -635,3 +673,14 @@ | |||
1895 | 635 | export_branch = Branch.open_from_transport( | 673 | export_branch = Branch.open_from_transport( |
1896 | 636 | root_transport.clone('thread1')) | 674 | root_transport.clone('thread1')) |
1897 | 637 | self.assertEqual('thread1-id', export_branch.last_revision()) | 675 | self.assertEqual('thread1-id', export_branch.last_revision()) |
1898 | 676 | |||
1899 | 677 | def test_set_nick_renames_thread(self): | ||
1900 | 678 | tree = self.get_tree_with_loom() | ||
1901 | 679 | tree.branch.new_thread(tree.branch.nick) | ||
1902 | 680 | orig_threads = tree.branch.get_loom_state().get_threads() | ||
1903 | 681 | new_thread_name = 'new thread name' | ||
1904 | 682 | tree.branch.nick = new_thread_name | ||
1905 | 683 | new_threads = tree.branch.get_loom_state().get_threads() | ||
1906 | 684 | self.assertNotEqual(orig_threads, new_threads) | ||
1907 | 685 | self.assertEqual(new_thread_name, new_threads[0][0]) | ||
1908 | 686 | self.assertEqual(new_thread_name, tree.branch.nick) | ||
1909 | 638 | 687 | ||
1910 | === modified file 'tests/test_revspec.py' | |||
1911 | --- tests/test_revspec.py 2008-09-12 01:55:17 +0000 | |||
1912 | +++ tests/test_revspec.py 2010-08-05 18:57:44 +0000 | |||
1913 | @@ -19,26 +19,29 @@ | |||
1914 | 19 | """Tests of the loom revision-specifiers.""" | 19 | """Tests of the loom revision-specifiers.""" |
1915 | 20 | 20 | ||
1916 | 21 | 21 | ||
1918 | 22 | import bzrlib | 22 | import bzrlib.errors |
1919 | 23 | from bzrlib.plugins.loom.branch import NoLowerThread, NoSuchThread | 23 | from bzrlib.plugins.loom.branch import NoLowerThread, NoSuchThread |
1920 | 24 | from bzrlib.plugins.loom.tests import TestCaseWithLoom | 24 | from bzrlib.plugins.loom.tests import TestCaseWithLoom |
1921 | 25 | import bzrlib.plugins.loom.tree | 25 | import bzrlib.plugins.loom.tree |
1922 | 26 | from bzrlib.revisionspec import RevisionSpec | 26 | from bzrlib.revisionspec import RevisionSpec |
1923 | 27 | 27 | ||
1924 | 28 | 28 | ||
1928 | 29 | class TestThreadRevSpec(TestCaseWithLoom): | 29 | class TestRevSpec(TestCaseWithLoom): |
1929 | 30 | """Tests of the ThreadRevisionSpecifier.""" | 30 | |
1927 | 31 | |||
1930 | 32 | def get_two_thread_loom(self): | 31 | def get_two_thread_loom(self): |
1931 | 33 | tree = self.get_tree_with_loom('source') | 32 | tree = self.get_tree_with_loom('source') |
1932 | 34 | tree.branch.new_thread('bottom') | 33 | tree.branch.new_thread('bottom') |
1933 | 35 | tree.branch.new_thread('top') | 34 | tree.branch.new_thread('top') |
1935 | 36 | tree.branch.nick = 'bottom' | 35 | tree.branch._set_nick('bottom') |
1936 | 37 | rev_id_bottom = tree.commit('change bottom') | 36 | rev_id_bottom = tree.commit('change bottom') |
1937 | 38 | loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) | 37 | loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) |
1938 | 39 | loom_tree.up_thread() | 38 | loom_tree.up_thread() |
1939 | 40 | rev_id_top = tree.commit('change top') | 39 | rev_id_top = tree.commit('change top') |
1940 | 41 | return tree, loom_tree, rev_id_bottom, rev_id_top | 40 | return tree, loom_tree, rev_id_bottom, rev_id_top |
1941 | 41 | |||
1942 | 42 | |||
1943 | 43 | class TestThreadRevSpec(TestRevSpec): | ||
1944 | 44 | """Tests of the ThreadRevisionSpecifier.""" | ||
1945 | 42 | 45 | ||
1946 | 43 | def test_thread_colon_at_bottom_errors(self): | 46 | def test_thread_colon_at_bottom_errors(self): |
1947 | 44 | tree, loom_tree, rev_id, _ = self.get_two_thread_loom() | 47 | tree, loom_tree, rev_id, _ = self.get_two_thread_loom() |
1948 | @@ -69,3 +72,44 @@ | |||
1949 | 69 | loom_tree.down_thread() | 72 | loom_tree.down_thread() |
1950 | 70 | spec = RevisionSpec.from_string('thread:top') | 73 | spec = RevisionSpec.from_string('thread:top') |
1951 | 71 | self.assertEqual(rev_id, spec.as_revision_id(tree.branch)) | 74 | self.assertEqual(rev_id, spec.as_revision_id(tree.branch)) |
1952 | 75 | |||
1953 | 76 | def test_thread_on_non_loom_gives_BzrError(self): | ||
1954 | 77 | tree = self.make_branch_and_tree('.') | ||
1955 | 78 | spec = RevisionSpec.from_string('thread:') | ||
1956 | 79 | err = self.assertRaises(bzrlib.errors.BzrError, spec.as_revision_id, | ||
1957 | 80 | tree.branch) | ||
1958 | 81 | self.assertFalse(err.internal_error) | ||
1959 | 82 | |||
1960 | 83 | |||
1961 | 84 | class TestBelowRevSpec(TestRevSpec): | ||
1962 | 85 | """Tests of the below: revision specifier.""" | ||
1963 | 86 | |||
1964 | 87 | def test_below_gets_tip_of_thread_below(self): | ||
1965 | 88 | tree, loom_tree, _, rev_id = self.get_two_thread_loom() | ||
1966 | 89 | loom_tree.down_thread() | ||
1967 | 90 | expected_id = tree.branch.last_revision() | ||
1968 | 91 | loom_tree.up_thread() | ||
1969 | 92 | spec = RevisionSpec.from_string('below:') | ||
1970 | 93 | self.assertEqual(expected_id, spec.as_revision_id(tree.branch)) | ||
1971 | 94 | |||
1972 | 95 | def test_below_on_bottom_thread_gives_BzrError(self): | ||
1973 | 96 | tree, loom_tree, _, rev_id = self.get_two_thread_loom() | ||
1974 | 97 | loom_tree.down_thread() | ||
1975 | 98 | spec = RevisionSpec.from_string('below:') | ||
1976 | 99 | err = self.assertRaises(bzrlib.errors.BzrError, spec.as_revision_id, | ||
1977 | 100 | tree.branch) | ||
1978 | 101 | self.assertFalse(err.internal_error) | ||
1979 | 102 | |||
1980 | 103 | def test_below_named_thread(self): | ||
1981 | 104 | tree, loom_tree, _, rev_id = self.get_two_thread_loom() | ||
1982 | 105 | loom_tree.down_thread() | ||
1983 | 106 | expected_id = tree.branch.last_revision() | ||
1984 | 107 | spec = RevisionSpec.from_string('below:top') | ||
1985 | 108 | self.assertEqual(expected_id, spec.as_revision_id(tree.branch)) | ||
1986 | 109 | |||
1987 | 110 | def test_below_on_non_loom_gives_BzrError(self): | ||
1988 | 111 | tree = self.make_branch_and_tree('.') | ||
1989 | 112 | spec = RevisionSpec.from_string('below:') | ||
1990 | 113 | err = self.assertRaises(bzrlib.errors.BzrError, spec.as_revision_id, | ||
1991 | 114 | tree.branch) | ||
1992 | 115 | self.assertFalse(err.internal_error) | ||
1993 | 72 | 116 | ||
1994 | === modified file 'tests/test_tree.py' | |||
1995 | --- tests/test_tree.py 2008-11-24 16:38:16 +0000 | |||
1996 | +++ tests/test_tree.py 2010-08-05 18:57:44 +0000 | |||
1997 | @@ -38,14 +38,14 @@ | |||
1998 | 38 | tree = self.get_tree_with_loom('source') | 38 | tree = self.get_tree_with_loom('source') |
1999 | 39 | tree.branch.new_thread('bottom') | 39 | tree.branch.new_thread('bottom') |
2000 | 40 | tree.branch.new_thread('top') | 40 | tree.branch.new_thread('top') |
2002 | 41 | tree.branch.nick = 'bottom' | 41 | tree.branch._set_nick('bottom') |
2003 | 42 | return bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) | 42 | return bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) |
2004 | 43 | 43 | ||
2005 | 44 | def test_down_thread(self): | 44 | def test_down_thread(self): |
2006 | 45 | tree = self.get_tree_with_loom('source') | 45 | tree = self.get_tree_with_loom('source') |
2007 | 46 | tree.branch.new_thread('bottom') | 46 | tree.branch.new_thread('bottom') |
2008 | 47 | tree.branch.new_thread('top') | 47 | tree.branch.new_thread('top') |
2010 | 48 | tree.branch.nick = 'top' | 48 | tree.branch._set_nick('top') |
2011 | 49 | loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) | 49 | loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) |
2012 | 50 | loom_tree.down_thread() | 50 | loom_tree.down_thread() |
2013 | 51 | self.assertEqual('bottom', tree.branch.nick) | 51 | self.assertEqual('bottom', tree.branch.nick) |
2014 | @@ -53,7 +53,7 @@ | |||
2015 | 53 | def _add_thread(self, tree, name): | 53 | def _add_thread(self, tree, name): |
2016 | 54 | """Create a new thread with a commit and return the commit id.""" | 54 | """Create a new thread with a commit and return the commit id.""" |
2017 | 55 | tree.branch.new_thread(name) | 55 | tree.branch.new_thread(name) |
2019 | 56 | tree.branch.nick = name | 56 | tree.branch._set_nick(name) |
2020 | 57 | return tree.commit(name) | 57 | return tree.commit(name) |
2021 | 58 | 58 | ||
2022 | 59 | def test_down_named_thread(self): | 59 | def test_down_named_thread(self): |
2023 | @@ -78,7 +78,7 @@ | |||
2024 | 78 | tree = self.get_tree_with_loom('tree') | 78 | tree = self.get_tree_with_loom('tree') |
2025 | 79 | tree.branch.new_thread('bottom') | 79 | tree.branch.new_thread('bottom') |
2026 | 80 | tree.branch.new_thread('top') | 80 | tree.branch.new_thread('top') |
2028 | 81 | tree.branch.nick = 'bottom' | 81 | tree.branch._set_nick('bottom') |
2029 | 82 | bottom_rev1 = tree.commit('bottom_commit') | 82 | bottom_rev1 = tree.commit('bottom_commit') |
2030 | 83 | tree_loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) | 83 | tree_loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) |
2031 | 84 | tree_loom_tree.up_thread() | 84 | tree_loom_tree.up_thread() |
2032 | @@ -89,10 +89,10 @@ | |||
2033 | 89 | """up-thread into a thread that already has this thread is a no-op.""" | 89 | """up-thread into a thread that already has this thread is a no-op.""" |
2034 | 90 | tree = self.get_tree_with_loom('tree') | 90 | tree = self.get_tree_with_loom('tree') |
2035 | 91 | tree.branch.new_thread('bottom') | 91 | tree.branch.new_thread('bottom') |
2037 | 92 | tree.branch.nick = 'bottom' | 92 | tree.branch._set_nick('bottom') |
2038 | 93 | bottom_rev1 = tree.commit('bottom_commit') | 93 | bottom_rev1 = tree.commit('bottom_commit') |
2039 | 94 | tree.branch.new_thread('top', 'bottom') | 94 | tree.branch.new_thread('top', 'bottom') |
2041 | 95 | tree.branch.nick = 'top' | 95 | tree.branch._set_nick('top') |
2042 | 96 | top_rev1 = tree.commit('top_commit', allow_pointless=True) | 96 | top_rev1 = tree.commit('top_commit', allow_pointless=True) |
2043 | 97 | tree_loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) | 97 | tree_loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) |
2044 | 98 | tree_loom_tree.down_thread() | 98 | tree_loom_tree.down_thread() |
2045 | @@ -108,10 +108,10 @@ | |||
2046 | 108 | """up-thread from a thread with new work.""" | 108 | """up-thread from a thread with new work.""" |
2047 | 109 | tree = self.get_tree_with_loom('tree') | 109 | tree = self.get_tree_with_loom('tree') |
2048 | 110 | tree.branch.new_thread('bottom') | 110 | tree.branch.new_thread('bottom') |
2050 | 111 | tree.branch.nick = 'bottom' | 111 | tree.branch._set_nick('bottom') |
2051 | 112 | bottom_rev1 = tree.commit('bottom_commit') | 112 | bottom_rev1 = tree.commit('bottom_commit') |
2052 | 113 | tree.branch.new_thread('top', 'bottom') | 113 | tree.branch.new_thread('top', 'bottom') |
2054 | 114 | tree.branch.nick = 'top' | 114 | tree.branch._set_nick('top') |
2055 | 115 | top_rev1 = tree.commit('top_commit', allow_pointless=True) | 115 | top_rev1 = tree.commit('top_commit', allow_pointless=True) |
2056 | 116 | tree_loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) | 116 | tree_loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) |
2057 | 117 | tree_loom_tree.down_thread() | 117 | tree_loom_tree.down_thread() |
2058 | @@ -144,14 +144,16 @@ | |||
2059 | 144 | self.build_tree_contents([('source/a', 'c')]) | 144 | self.build_tree_contents([('source/a', 'c')]) |
2060 | 145 | loom_tree.tree.commit('content to c') | 145 | loom_tree.tree.commit('content to c') |
2061 | 146 | loom_tree.up_thread(_mod_merge.WeaveMerger) | 146 | loom_tree.up_thread(_mod_merge.WeaveMerger) |
2063 | 147 | self.failIfExists('source/a.BASE') | 147 | # Disabled because WeaveMerger writes BASE files now. XXX: Figure out |
2064 | 148 | # how to test this actually worked, again. | ||
2065 | 149 | # self.failIfExists('source/a.BASE') | ||
2066 | 148 | 150 | ||
2067 | 149 | def get_loom_with_three_threads(self): | 151 | def get_loom_with_three_threads(self): |
2068 | 150 | tree = self.get_tree_with_loom('source') | 152 | tree = self.get_tree_with_loom('source') |
2069 | 151 | tree.branch.new_thread('bottom') | 153 | tree.branch.new_thread('bottom') |
2070 | 152 | tree.branch.new_thread('middle') | 154 | tree.branch.new_thread('middle') |
2071 | 153 | tree.branch.new_thread('top') | 155 | tree.branch.new_thread('top') |
2073 | 154 | tree.branch.nick = 'bottom' | 156 | tree.branch._set_nick('bottom') |
2074 | 155 | return bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) | 157 | return bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) |
2075 | 156 | 158 | ||
2076 | 157 | def test_up_many(self): | 159 | def test_up_many(self): |
2077 | @@ -191,12 +193,26 @@ | |||
2078 | 191 | self.assertEqual(['middle-1', 'bottom-2'], tree.get_parent_ids()) | 193 | self.assertEqual(['middle-1', 'bottom-2'], tree.get_parent_ids()) |
2079 | 192 | self.assertEqual(1, len(tree.conflicts())) | 194 | self.assertEqual(1, len(tree.conflicts())) |
2080 | 193 | 195 | ||
2081 | 196 | def test_up_many_target_thread(self): | ||
2082 | 197 | loom_tree = self.get_loom_with_three_threads() | ||
2083 | 198 | tree = loom_tree.tree | ||
2084 | 199 | loom_tree.up_many(target_thread='middle') | ||
2085 | 200 | self.assertEqual('middle', tree.branch.nick) | ||
2086 | 201 | |||
2087 | 202 | def test_up_many_target_thread_lower(self): | ||
2088 | 203 | loom_tree = self.get_loom_with_three_threads() | ||
2089 | 204 | tree = loom_tree.tree | ||
2090 | 205 | loom_tree.up_many(target_thread='top') | ||
2091 | 206 | e = self.assertRaises(errors.BzrCommandError, | ||
2092 | 207 | loom_tree.up_many, target_thread='middle') | ||
2093 | 208 | self.assertEqual('Cannot up-thread to lower thread.', str(e)) | ||
2094 | 209 | |||
2095 | 194 | def test_revert_loom(self): | 210 | def test_revert_loom(self): |
2096 | 195 | tree = self.get_tree_with_loom(',') | 211 | tree = self.get_tree_with_loom(',') |
2097 | 196 | # ensure we have some stuff to revert | 212 | # ensure we have some stuff to revert |
2098 | 197 | tree.branch.new_thread('foo') | 213 | tree.branch.new_thread('foo') |
2099 | 198 | tree.branch.new_thread('bar') | 214 | tree.branch.new_thread('bar') |
2101 | 199 | tree.branch.nick = 'bar' | 215 | tree.branch._set_nick('bar') |
2102 | 200 | tree.commit('change something', allow_pointless=True) | 216 | tree.commit('change something', allow_pointless=True) |
2103 | 201 | loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) | 217 | loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) |
2104 | 202 | loom_tree.revert_loom() | 218 | loom_tree.revert_loom() |
2105 | @@ -211,7 +227,7 @@ | |||
2106 | 211 | # ensure we have some stuff to revert | 227 | # ensure we have some stuff to revert |
2107 | 212 | tree.branch.new_thread('foo') | 228 | tree.branch.new_thread('foo') |
2108 | 213 | tree.branch.new_thread('bar') | 229 | tree.branch.new_thread('bar') |
2110 | 214 | tree.branch.nick = 'bar' | 230 | tree.branch._set_nick('bar') |
2111 | 215 | tree.commit('change something', allow_pointless=True) | 231 | tree.commit('change something', allow_pointless=True) |
2112 | 216 | loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) | 232 | loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) |
2113 | 217 | loom_tree.revert_loom(thread='bar') | 233 | loom_tree.revert_loom(thread='bar') |
2114 | @@ -228,7 +244,7 @@ | |||
2115 | 228 | # ensure we have some stuff to revert | 244 | # ensure we have some stuff to revert |
2116 | 229 | tree.branch.new_thread('foo') | 245 | tree.branch.new_thread('foo') |
2117 | 230 | tree.branch.new_thread('bar') | 246 | tree.branch.new_thread('bar') |
2119 | 231 | tree.branch.nick = 'bar' | 247 | tree.branch._set_nick('bar') |
2120 | 232 | tree.commit('change something', allow_pointless=True) | 248 | tree.commit('change something', allow_pointless=True) |
2121 | 233 | loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) | 249 | loom_tree = bzrlib.plugins.loom.tree.LoomTreeDecorator(tree) |
2122 | 234 | loom_tree.revert_loom(thread='foo') | 250 | loom_tree.revert_loom(thread='foo') |
2123 | 235 | 251 | ||
2124 | === modified file 'tree.py' | |||
2125 | --- tree.py 2008-11-24 16:38:16 +0000 | |||
2126 | +++ tree.py 2010-08-05 18:57:44 +0000 | |||
2127 | @@ -24,7 +24,10 @@ | |||
2128 | 24 | __all__ = ['LoomTreeDecorator'] | 24 | __all__ = ['LoomTreeDecorator'] |
2129 | 25 | 25 | ||
2130 | 26 | 26 | ||
2132 | 27 | from bzrlib import ui | 27 | from bzrlib import ( |
2133 | 28 | trace, | ||
2134 | 29 | ui, | ||
2135 | 30 | ) | ||
2136 | 28 | from bzrlib.decorators import needs_write_lock | 31 | from bzrlib.decorators import needs_write_lock |
2137 | 29 | import bzrlib.errors | 32 | import bzrlib.errors |
2138 | 30 | import bzrlib.revision | 33 | import bzrlib.revision |
2139 | @@ -78,7 +81,7 @@ | |||
2140 | 78 | graph = self.tree.branch.repository.get_graph() | 81 | graph = self.tree.branch.repository.get_graph() |
2141 | 79 | # special case no-change condition. | 82 | # special case no-change condition. |
2142 | 80 | if new_thread_rev == old_thread_rev: | 83 | if new_thread_rev == old_thread_rev: |
2144 | 81 | self.tree.branch.nick = new_thread_name | 84 | self.tree.branch._set_nick(new_thread_name) |
2145 | 82 | return 0 | 85 | return 0 |
2146 | 83 | if new_thread_rev == EMPTY_REVISION: | 86 | if new_thread_rev == EMPTY_REVISION: |
2147 | 84 | new_thread_rev = bzrlib.revision.NULL_REVISION | 87 | new_thread_rev = bzrlib.revision.NULL_REVISION |
2148 | @@ -87,19 +90,15 @@ | |||
2149 | 87 | # merge the tree up into the new patch: | 90 | # merge the tree up into the new patch: |
2150 | 88 | if merge_type is None: | 91 | if merge_type is None: |
2151 | 89 | merge_type = bzrlib.merge.Merge3Merger | 92 | merge_type = bzrlib.merge.Merge3Merger |
2152 | 90 | pb = ui.ui_factory.nested_progress_bar() | ||
2153 | 91 | try: | 93 | try: |
2165 | 92 | try: | 94 | merge_controller = bzrlib.merge.Merger.from_revision_ids( |
2166 | 93 | merge_controller = bzrlib.merge.Merger.from_revision_ids( | 95 | None, self.tree, new_thread_rev, revision_graph=graph) |
2167 | 94 | pb, self.tree, new_thread_rev, revision_graph=graph) | 96 | except bzrlib.errors.UnrelatedBranches: |
2168 | 95 | except bzrlib.errors.UnrelatedBranches: | 97 | raise bzrlib.errors.BzrCommandError('corrupt loom: thread %s' |
2169 | 96 | raise bzrlib.errors.BzrCommandError('corrupt loom: thread %s' | 98 | ' has no common ancestor with thread %s' |
2170 | 97 | ' has no common ancestor with thread %s' | 99 | % (new_thread_name, threadname)) |
2171 | 98 | % (new_thread_name, threadname)) | 100 | merge_controller.merge_type = merge_type |
2172 | 99 | merge_controller.merge_type = merge_type | 101 | result = merge_controller.do_merge() |
2162 | 100 | result = merge_controller.do_merge() | ||
2163 | 101 | finally: | ||
2164 | 102 | pb.finished() | ||
2173 | 103 | # change the tree to the revision of the new thread. | 102 | # change the tree to the revision of the new thread. |
2174 | 104 | parent_trees = [] | 103 | parent_trees = [] |
2175 | 105 | if new_thread_rev != bzrlib.revision.NULL_REVISION: | 104 | if new_thread_rev != bzrlib.revision.NULL_REVISION: |
2176 | @@ -126,20 +125,37 @@ | |||
2177 | 126 | # change the branch | 125 | # change the branch |
2178 | 127 | self.tree.branch.generate_revision_history(new_thread_rev) | 126 | self.tree.branch.generate_revision_history(new_thread_rev) |
2179 | 128 | # update the branch nick. | 127 | # update the branch nick. |
2182 | 129 | self.tree.branch.nick = new_thread_name | 128 | self.tree.branch._set_nick(new_thread_name) |
2183 | 130 | bzrlib.trace.note("Moved to thread '%s'." % new_thread_name) | 129 | trace.note("Moved to thread '%s'." % new_thread_name) |
2184 | 130 | if (basis_tree is not None and | ||
2185 | 131 | not result and not | ||
2186 | 132 | self.tree.changes_from(basis_tree).has_changed()): | ||
2187 | 133 | trace.note("This thread is now empty, you may wish to " | ||
2188 | 134 | 'run "bzr combine-thread" to remove it.') | ||
2189 | 131 | if result != 0: | 135 | if result != 0: |
2190 | 132 | return 1 | 136 | return 1 |
2191 | 133 | else: | 137 | else: |
2192 | 134 | return 0 | 138 | return 0 |
2193 | 135 | 139 | ||
2198 | 136 | def up_many(self, merge_type=None): | 140 | def up_many(self, merge_type=None, target_thread=None): |
2199 | 137 | threads = self.branch.get_loom_state().get_threads() | 141 | loom_state = self.branch.get_loom_state() |
2200 | 138 | top_thread_name = threads[-1][0] | 142 | threads = loom_state.get_threads() |
2201 | 139 | while self.branch.nick != top_thread_name: | 143 | if target_thread is None: |
2202 | 144 | target_thread = threads[-1][0] | ||
2203 | 145 | if self.branch.nick == target_thread: | ||
2204 | 146 | raise bzrlib.errors.BzrCommandError( | ||
2205 | 147 | 'Cannot move up from the highest thread.') | ||
2206 | 148 | else: | ||
2207 | 149 | upper_thread_i = loom_state.thread_index(target_thread) | ||
2208 | 150 | lower_thread_i = loom_state.thread_index(self.branch.nick) | ||
2209 | 151 | if lower_thread_i > upper_thread_i: | ||
2210 | 152 | raise bzrlib.errors.BzrCommandError( | ||
2211 | 153 | "Cannot up-thread to lower thread.") | ||
2212 | 154 | while self.branch.nick != target_thread: | ||
2213 | 140 | old_nick = self.branch.nick | 155 | old_nick = self.branch.nick |
2216 | 141 | if self.up_thread(merge_type) != 0: | 156 | result = self.up_thread(merge_type) |
2217 | 142 | break | 157 | if result != 0: |
2218 | 158 | return result | ||
2219 | 143 | if len(self.tree.get_parent_ids()) > 1: | 159 | if len(self.tree.get_parent_ids()) > 1: |
2220 | 144 | self.tree.commit('Merge %s into %s' % (old_nick, | 160 | self.tree.commit('Merge %s into %s' % (old_nick, |
2221 | 145 | self.branch.nick)) | 161 | self.branch.nick)) |
2222 | @@ -147,12 +163,11 @@ | |||
2223 | 147 | @needs_write_lock | 163 | @needs_write_lock |
2224 | 148 | def down_thread(self, name=None): | 164 | def down_thread(self, name=None): |
2225 | 149 | """Move to a thread down in the loom. | 165 | """Move to a thread down in the loom. |
2227 | 150 | 166 | ||
2228 | 151 | :param name: If None, use the next lower thread; otherwise the nae of | 167 | :param name: If None, use the next lower thread; otherwise the nae of |
2229 | 152 | the thread to move to. | 168 | the thread to move to. |
2230 | 153 | """ | 169 | """ |
2231 | 154 | self._check_switch() | 170 | self._check_switch() |
2232 | 155 | current_revision = self.tree.last_revision() | ||
2233 | 156 | threadname = self.tree.branch.nick | 171 | threadname = self.tree.branch.nick |
2234 | 157 | state = self.tree.branch.get_loom_state() | 172 | state = self.tree.branch.get_loom_state() |
2235 | 158 | threads = state.get_threads() | 173 | threads = state.get_threads() |
2236 | @@ -168,7 +183,7 @@ | |||
2237 | 168 | index = state.thread_index(name) | 183 | index = state.thread_index(name) |
2238 | 169 | new_thread_rev = threads[index][1] | 184 | new_thread_rev = threads[index][1] |
2239 | 170 | assert new_thread_rev is not None | 185 | assert new_thread_rev is not None |
2241 | 171 | self.tree.branch.nick = new_thread_name | 186 | self.tree.branch._set_nick(new_thread_name) |
2242 | 172 | if new_thread_rev == old_thread_rev: | 187 | if new_thread_rev == old_thread_rev: |
2243 | 173 | # fast path no-op changes | 188 | # fast path no-op changes |
2244 | 174 | bzrlib.trace.note("Moved to thread '%s'." % new_thread_name) | 189 | bzrlib.trace.note("Moved to thread '%s'." % new_thread_name) |
2245 | @@ -177,17 +192,30 @@ | |||
2246 | 177 | new_thread_rev = bzrlib.revision.NULL_REVISION | 192 | new_thread_rev = bzrlib.revision.NULL_REVISION |
2247 | 178 | if old_thread_rev == EMPTY_REVISION: | 193 | if old_thread_rev == EMPTY_REVISION: |
2248 | 179 | old_thread_rev = bzrlib.revision.NULL_REVISION | 194 | old_thread_rev = bzrlib.revision.NULL_REVISION |
2251 | 180 | basis_tree = self.tree.branch.repository.revision_tree(old_thread_rev) | 195 | repository = self.tree.branch.repository |
2252 | 181 | to_tree = self.tree.branch.repository.revision_tree(new_thread_rev) | 196 | try: |
2253 | 197 | basis_tree = self.tree.revision_tree(old_thread_rev) | ||
2254 | 198 | except bzrlib.errors.NoSuchRevisionInTree: | ||
2255 | 199 | basis_tree = repository.revision_tree(old_thread_rev) | ||
2256 | 200 | to_tree = repository.revision_tree(new_thread_rev) | ||
2257 | 182 | result = bzrlib.merge.merge_inner(self.tree.branch, | 201 | result = bzrlib.merge.merge_inner(self.tree.branch, |
2258 | 183 | to_tree, | 202 | to_tree, |
2259 | 184 | basis_tree, | 203 | basis_tree, |
2260 | 185 | this_tree=self.tree) | 204 | this_tree=self.tree) |
2263 | 186 | self.tree.branch.generate_revision_history(new_thread_rev) | 205 | branch_revno, branch_revision = self.tree.branch.last_revision_info() |
2264 | 187 | self.tree.set_last_revision(new_thread_rev) | 206 | graph = repository.get_graph() |
2265 | 207 | new_thread_revno = graph.find_distance_to_null(new_thread_rev, | ||
2266 | 208 | [(branch_revision, branch_revno)]) | ||
2267 | 209 | self.tree.branch.set_last_revision_info(new_thread_revno, | ||
2268 | 210 | new_thread_rev) | ||
2269 | 211 | if new_thread_rev == bzrlib.revision.NULL_REVISION: | ||
2270 | 212 | parent_list = [] | ||
2271 | 213 | else: | ||
2272 | 214 | parent_list = [(new_thread_rev, to_tree)] | ||
2273 | 215 | self.tree.set_parent_trees(parent_list) | ||
2274 | 188 | bzrlib.trace.note("Moved to thread '%s'." % new_thread_name) | 216 | bzrlib.trace.note("Moved to thread '%s'." % new_thread_name) |
2275 | 189 | return result | 217 | return result |
2277 | 190 | 218 | ||
2278 | 191 | def lock_write(self): | 219 | def lock_write(self): |
2279 | 192 | self.tree.lock_write() | 220 | self.tree.lock_write() |
2280 | 193 | 221 | ||
2281 | 194 | 222 | ||
2282 | === added file 'version.py' | |||
2283 | --- version.py 1970-01-01 00:00:00 +0000 | |||
2284 | +++ version.py 2010-08-05 18:57:44 +0000 | |||
2285 | @@ -0,0 +1,28 @@ | |||
2286 | 1 | # Loom, a plugin for bzr to assist in developing focused patches. | ||
2287 | 2 | # Copyright (C) 2010 Canonical Limited. | ||
2288 | 3 | # | ||
2289 | 4 | # This program is free software; you can redistribute it and/or modify | ||
2290 | 5 | # it under the terms of the GNU General Public License version 2 as published | ||
2291 | 6 | # by the Free Software Foundation. | ||
2292 | 7 | # | ||
2293 | 8 | # This program is distributed in the hope that it will be useful, | ||
2294 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2295 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2296 | 11 | # GNU General Public License for more details. | ||
2297 | 12 | # | ||
2298 | 13 | # You should have received a copy of the GNU General Public License | ||
2299 | 14 | # along with this program; if not, write to the Free Software | ||
2300 | 15 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
2301 | 16 | # | ||
2302 | 17 | |||
2303 | 18 | """Versioning information for bzr-loom.""" | ||
2304 | 19 | |||
2305 | 20 | __all__ = [ | ||
2306 | 21 | 'bzr_plugin_version', | ||
2307 | 22 | 'bzr_minimum_version', | ||
2308 | 23 | 'bzr_maximum_version', | ||
2309 | 24 | ] | ||
2310 | 25 | |||
2311 | 26 | bzr_plugin_version = (2, 2, 1, 'dev', 0) | ||
2312 | 27 | bzr_minimum_version = (2, 2, 0) | ||
2313 | 28 | bzr_maximum_version = None |