Merge lp:~spiv/bzr-builder/merge-subdirs-479705 into lp:bzr-builder
- merge-subdirs-479705
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 99 | ||||
Proposed branch: | lp:~spiv/bzr-builder/merge-subdirs-479705 | ||||
Merge into: | lp:bzr-builder | ||||
Diff against target: |
471 lines (+254/-32) 3 files modified
__init__.py (+23/-1) recipe.py (+109/-4) tests/test_recipe.py (+122/-27) |
||||
To merge this branch: | bzr merge lp:~spiv/bzr-builder/merge-subdirs-479705 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Westby | Needs Fixing | ||
Review via email: mp+31251@code.launchpad.net |
Commit message
Add 'nest-part' instruction to recipes.
Description of the change
See discussion so far at <https:/
In summary: this adds a "nest-part" instruction built on the MergeIntoMerger that is being added to bzr 2.2. This will support the use case of merging just "debian/" into an upstream branch even when the packaging branch is a parallel import vs. upstream.
- 71. By Andrew Bennetts
-
Merge trunk.
Martin Pool (mbp) wrote : | # |
What do you mean by "no plans to provide a new bzr in old releases"? Is the
ppa not enough? Should we push to get it into backports or something?
- Martin
On 30/07/2010 7:56 AM, "James Westby"
<<email address hidden><jw%<email address hidden>>>
wrote:
Review: Needs Fixing
Hi,
The launchpad team have no plans to provide a newer bzr in older
releases, so I would like to proceed by copying the code in to bzr-builder
where needed.
As for the changes proposed here, they are largely fine.
nest-part packaging lp:~foo-dev/foo/packaging -1 debian
I would like to move the revision spec to the end of the line and make it
optional.
I don't mind too much whether it or the target subdir comes first.
134 + op = OperationWithCl
I don't think we can rely on OperationWithCl
so either we copy that too, or we do it the old fashioned way.
Thanks for your work on this. Let me know if you would like me to
make these changes.
Thanks,
James
--
https:/
Your team canoni...
James Westby (james-w) wrote : | # |
On Thu, 29 Jul 2010 22:16:41 -0000, Martin Pool <email address hidden> wrote:
> What do you mean by "no plans to provide a new bzr in old releases"? Is the
> ppa not enough? Should we push to get it into backports or something?
https:/
They don't plan to put the work in to providing an older bzr to the
builders when doing recipe builds.
Thanks,
James
Martin Pool (mbp) wrote : | # |
But they are going to provide a backported bzr-builder (or to run it from a checkout?) That seems a bit strange, but perhaps it's most pragmatic approach.
James Westby (james-w) wrote : | # |
On Thu, 05 Aug 2010 01:28:43 -0000, Martin Pool <email address hidden> wrote:
> But they are going to provide a backported bzr-builder (or to run it
> from a checkout?) That seems a bit strange, but perhaps it's most
> pragmatic approach.
They are. However, the other day Aaron was asking about running
bzr-builder outside the chroot so that only one version was used. That
would also enable us to use a newer bzr without backporting. I don't
know if he decided to do this or not.
Thanks,
James
Aaron Bentley (abentley) wrote : | # |
We are planning to run bzr-builder outside the chroot. That should reduce the risk of new bzr's contaminating daily builds for old releases, reduce the amount of maintenance required when new releases of bzr-builder become available, and allow us to build from 2a branches into ancient, but still supported, distros like Dapper.
Andrew Bennetts (spiv) wrote : | # |
> Thanks for your work on this. Let me know if you would like me to
> make these changes.
The backport of 2.2's MergeIntoMerger and removal of OperationWithCl
I'm working on the requested syntax change now.
Andrew Bennetts (spiv) wrote : | # |
The syntax change is now done. Please review.
- 72. By Andrew Bennetts
-
Change the syntax of nest-part to have the revspec at the end, and make it optional.
- 73. By Andrew Bennetts
-
Tweak plugin help text.
Preview Diff
1 | === modified file '__init__.py' |
2 | --- __init__.py 2010-07-09 19:23:36 +0000 |
3 | +++ __init__.py 2010-08-12 13:41:15 +0000 |
4 | @@ -21,7 +21,7 @@ |
5 | |
6 | A recipe is just a text file that starts with a line such as:: |
7 | |
8 | - # bzr-builder format 0.2 deb-version 1.0+{revno}-{revno:packaging} |
9 | + # bzr-builder format 0.3 deb-version 1.0+{revno}-{revno:packaging} |
10 | |
11 | The format specifier is there to allow the syntax to be changed in later |
12 | versions, and the meaning of "deb-version" will be explained later. |
13 | @@ -70,6 +70,20 @@ |
14 | for the revisionspec is identical to that taken by the "--revision" argument |
15 | to many bzr commands. See "bzr help revisionspec" for details. |
16 | |
17 | +You can also merge specific subdirectories from a branch with a "nest-part" |
18 | +line like |
19 | + |
20 | +nest-part packaging lp:~foo-dev/foo/packaging debian |
21 | + |
22 | +which specifies that the only the debian/ subdirectory should be merged. This |
23 | +works even if the branches share no revision history. You can optionally |
24 | +specify the revision and subdirectory in the target with a line like |
25 | + |
26 | +nest-part libfoo lp:libfoo src lib/foo tag:release-1.2 |
27 | + |
28 | +will put the "src" directory of libfoo in "lib/foo", using the revision of |
29 | +libfoo tagged "release-1.2" |
30 | + |
31 | It is also possible to run an arbitrary command at a particular point in the |
32 | construction process. For example:: |
33 | |
34 | @@ -114,10 +128,18 @@ |
35 | tree is built the top of debian/changelog has a version number of |
36 | "1.0-1" then this would evaluate to "1.0". |
37 | |
38 | +Instruction syntax summary: |
39 | + |
40 | + * nest NAME BRANCH TARGET-DIR [REVISION] |
41 | + * merge NAME BRANCH [REVISION] |
42 | + * nest-part NAME BRANCH SUBDIR [TARGET-DIR [REVISION]] |
43 | + * run COMMAND |
44 | + |
45 | Format versions: |
46 | |
47 | 0.1 - original format. |
48 | 0.2 - added "run" instruction. |
49 | + 0.3 - added "nest-part" instruction. |
50 | """ |
51 | |
52 | if __name__ == '__main__': |
53 | |
54 | === modified file 'recipe.py' |
55 | --- recipe.py 2010-07-09 20:51:37 +0000 |
56 | +++ recipe.py 2010-08-12 13:41:15 +0000 |
57 | @@ -22,6 +22,7 @@ |
58 | bzrdir, |
59 | errors, |
60 | merge, |
61 | + revision, |
62 | revisionspec, |
63 | tag, |
64 | trace, |
65 | @@ -41,6 +42,7 @@ |
66 | |
67 | |
68 | MERGE_INSTRUCTION = "merge" |
69 | +NEST_PART_INSTRUCTION = "nest-part" |
70 | NEST_INSTRUCTION = "nest" |
71 | RUN_INSTRUCTION = "run" |
72 | |
73 | @@ -223,6 +225,47 @@ |
74 | child_branch.branch.unlock() |
75 | |
76 | |
77 | +def nest_part_branch(op, child_branch, tree_to, br_to, subpath, |
78 | + target_subdir=None): |
79 | + """Merge the branch subdirectory specified by child_branch. |
80 | + |
81 | + :param child_branch: the RecipeBranch to retrieve the branch and revision to |
82 | + merge from. |
83 | + :param tree_to: the WorkingTree to merge in to. |
84 | + :param br_to: the Branch to merge in to. |
85 | + :param subpath: only merge files from branch that are from this path. |
86 | + e.g. subpath='/debian' will only merge changes from that directory. |
87 | + :param target_subdir: (optional) directory in target to merge that |
88 | + subpath into. Defaults to basename of subpath. |
89 | + """ |
90 | + merge_from = branch.Branch.open(child_branch.url) |
91 | + merge_from.lock_read() |
92 | + op.add_cleanup(merge_from.unlock) |
93 | + if child_branch.revspec is not None: |
94 | + merge_revspec = revisionspec.RevisionSpec.from_string( |
95 | + child_branch.revspec) |
96 | + merge_revid = merge_revspec.as_revision_id(merge_from) |
97 | + else: |
98 | + merge_revid = merge_from.last_revision() |
99 | + child_branch.revid = merge_revid |
100 | + other_tree = merge_from.basis_tree() |
101 | + other_tree.lock_read() |
102 | + op.add_cleanup(other_tree.unlock) |
103 | + if target_subdir is None: |
104 | + target_subdir = os.path.basename(subpath) |
105 | + merger = merge.MergeIntoMerger(this_tree=tree_to, other_tree=other_tree, |
106 | + other_branch=merge_from, target_subdir=target_subdir, |
107 | + source_subpath=subpath, other_rev_id=merge_revid) |
108 | + merger.set_base_revision(revision.NULL_REVISION, merge_from) |
109 | + conflict_count = merger.do_merge() |
110 | + merger.set_pending() |
111 | + if conflict_count: |
112 | + # FIXME: better reporting |
113 | + raise errors.BzrCommandError("Conflicts from merge") |
114 | + tree_to.commit("Merge %s of %s" % |
115 | + (subpath, urlutils.unescape_for_display(child_branch.url, 'utf-8'))) |
116 | + |
117 | + |
118 | def update_branch(base_branch, tree_to, br_to, to_transport, |
119 | possible_transports=None): |
120 | if base_branch.branch is None: |
121 | @@ -422,6 +465,20 @@ |
122 | possible_transports=possible_transports) |
123 | |
124 | |
125 | +class NestPartInstruction(ChildBranch): |
126 | + |
127 | + def __init__(self, recipe_branch, subpath, target_subdir): |
128 | + ChildBranch.__init__(self, recipe_branch) |
129 | + self.subpath = subpath |
130 | + self.target_subdir = target_subdir |
131 | + |
132 | + def apply(self, target_path, tree_to, br_to): |
133 | + from bzrlib.cleanup import OperationWithCleanups |
134 | + op = OperationWithCleanups(nest_part_branch) |
135 | + op.run(self.recipe_branch, tree_to, br_to, self.subpath, |
136 | + self.target_subdir) |
137 | + |
138 | + |
139 | class NestInstruction(ChildBranch): |
140 | |
141 | def apply(self, target_path, tree_to, br_to, possible_transports=None): |
142 | @@ -440,6 +497,9 @@ |
143 | The child_branches attribute is a list of tuples of ChildBranch objects. |
144 | The revid attribute records the revid that the url and revspec resolved |
145 | to when the RecipeBranch was built, or None if it has not been built. |
146 | + |
147 | + :ivar revid: after this recipe branch has been built this is set to the |
148 | + revision ID that was merged/nested from the branch at self.url. |
149 | """ |
150 | |
151 | def __init__(self, name, url, revspec=None): |
152 | @@ -464,6 +524,18 @@ |
153 | """ |
154 | self.child_branches.append(MergeInstruction(branch)) |
155 | |
156 | + def nest_part_branch(self, branch, subpath=None, target_subdir=None): |
157 | + """Merge subdir of a child branch into this one. |
158 | + |
159 | + :param branch: the RecipeBranch to merge. |
160 | + :param subpath: only merge files from branch that are from this path. |
161 | + e.g. subpath='/debian' will only merge changes from that directory. |
162 | + :param target_subdir: (optional) directory in target to merge that |
163 | + subpath into. Defaults to basename of subpath. |
164 | + """ |
165 | + self.child_branches.append( |
166 | + NestPartInstruction(branch, subpath, target_subdir)) |
167 | + |
168 | def nest_branch(self, location, branch): |
169 | """Nest a child branch in to this one. |
170 | |
171 | @@ -663,7 +735,7 @@ |
172 | eol_char = "\n" |
173 | digit_chars = ("0", "1", "2", "3", "4", "5", "6", "7", "8", "9") |
174 | |
175 | - NEWEST_VERSION = 0.2 |
176 | + NEWEST_VERSION = 0.3 |
177 | |
178 | def __init__(self, f, filename=None): |
179 | """Create a RecipeParser. |
180 | @@ -741,13 +813,25 @@ |
181 | url = self.parse_branch_url() |
182 | if instruction == NEST_INSTRUCTION: |
183 | location = self.parse_branch_location() |
184 | - revspec = self.parse_optional_revspec() |
185 | + if instruction == NEST_PART_INSTRUCTION: |
186 | + path = self.parse_subpath() |
187 | + target_subdir = self.parse_optional_path() |
188 | + if target_subdir == '': |
189 | + target_subdir = None |
190 | + revspec = None |
191 | + else: |
192 | + revspec = self.parse_optional_revspec() |
193 | + else: |
194 | + revspec = self.parse_optional_revspec() |
195 | self.new_line() |
196 | last_branch = RecipeBranch(branch_id, url, revspec=revspec) |
197 | if instruction == NEST_INSTRUCTION: |
198 | active_branches[-1].nest_branch(location, last_branch) |
199 | - else: |
200 | + elif instruction == MERGE_INSTRUCTION: |
201 | active_branches[-1].merge_branch(last_branch) |
202 | + elif instruction == NEST_PART_INSTRUCTION: |
203 | + active_branches[-1].nest_part_branch( |
204 | + last_branch, path, target_subdir) |
205 | last_instruction = instruction |
206 | if len(active_branches) == 0: |
207 | self.throw_parse_error("Empty recipe") |
208 | @@ -771,9 +855,13 @@ |
209 | if self.version < 0.2: |
210 | options = (MERGE_INSTRUCTION, NEST_INSTRUCTION) |
211 | options_str = "'%s' or '%s'" % options |
212 | - else: |
213 | + elif self.version < 0.3: |
214 | options = (MERGE_INSTRUCTION, NEST_INSTRUCTION, RUN_INSTRUCTION) |
215 | options_str = "'%s', '%s' or '%s'" % options |
216 | + else: |
217 | + options = (MERGE_INSTRUCTION, NEST_INSTRUCTION, |
218 | + NEST_PART_INSTRUCTION, RUN_INSTRUCTION) |
219 | + options_str = "'%s', '%s', '%s' or '%s'" % options |
220 | instruction = self.peek_to_whitespace() |
221 | if instruction is None: |
222 | self.throw_parse_error("End of line while looking for %s" |
223 | @@ -830,6 +918,16 @@ |
224 | self.seen_paths[norm_location] = self.line_index + 1 |
225 | return location |
226 | |
227 | + def parse_subpath(self): |
228 | + self.parse_whitespace("the subpath to merge") |
229 | + location = self.take_to_whitespace("the subpath to merge") |
230 | + return location |
231 | + |
232 | + def parse_revspec(self): |
233 | + self.parse_whitespace("the revspec") |
234 | + revspec = self.take_to_whitespace("the revspec") |
235 | + return revspec |
236 | + |
237 | def parse_optional_revspec(self): |
238 | self.parse_whitespace(None, require=False) |
239 | revspec = self.peek_to_whitespace() |
240 | @@ -837,6 +935,13 @@ |
241 | self.take_chars(len(revspec)) |
242 | return revspec |
243 | |
244 | + def parse_optional_path(self): |
245 | + self.parse_whitespace(None, require=False) |
246 | + path = self.peek_to_whitespace() |
247 | + if path is not None: |
248 | + self.take_chars(len(path)) |
249 | + return path |
250 | + |
251 | def throw_parse_error(self, problem, cls=None, **kwargs): |
252 | if cls is None: |
253 | cls = RecipeParseError |
254 | |
255 | === modified file 'tests/test_recipe.py' |
256 | --- tests/test_recipe.py 2010-07-09 20:51:37 +0000 |
257 | +++ tests/test_recipe.py 2010-08-12 13:41:15 +0000 |
258 | @@ -42,7 +42,7 @@ |
259 | class RecipeParserTests(TestCaseInTempDir): |
260 | |
261 | deb_version = "0.1-{revno}" |
262 | - basic_header = ("# bzr-builder format 0.2 deb-version " |
263 | + basic_header = ("# bzr-builder format 0.3 deb-version " |
264 | + deb_version +"\n") |
265 | basic_branch = "http://foo.org/" |
266 | basic_header_and_branch = basic_header + basic_branch + "\n" |
267 | @@ -133,8 +133,8 @@ |
268 | self.basic_header + "http://foo.org/ 2 foo") |
269 | |
270 | def tests_rejects_unknown_instruction(self): |
271 | - self.assertParseError(3, 1, "Expecting 'merge', 'nest' or 'run', " |
272 | - "got 'cat'", self.get_recipe, |
273 | + self.assertParseError(3, 1, "Expecting 'merge', 'nest', 'nest-part' " |
274 | + "or 'run', got 'cat'", self.get_recipe, |
275 | self.basic_header + "http://foo.org/\n" + "cat") |
276 | |
277 | def test_rejects_merge_no_name(self): |
278 | @@ -152,6 +152,21 @@ |
279 | "got 'bar'", self.get_recipe, |
280 | self.basic_header_and_branch + "merge foo url 2 bar") |
281 | |
282 | + def test_rejects_nest_part_no_name(self): |
283 | + self.assertParseError(3, 11, "End of line while looking for " |
284 | + "the branch id", self.get_recipe, |
285 | + self.basic_header_and_branch + "nest-part ") |
286 | + |
287 | + def test_rejects_nest_part_no_url(self): |
288 | + self.assertParseError(3, 15, "End of line while looking for " |
289 | + "the branch url", self.get_recipe, |
290 | + self.basic_header_and_branch + "nest-part foo ") |
291 | + |
292 | + def test_rejects_nest_part_no_subpath(self): |
293 | + self.assertParseError(3, 22, "End of line while looking for " |
294 | + "the subpath to merge", self.get_recipe, |
295 | + self.basic_header_and_branch + "nest-part foo url:// ") |
296 | + |
297 | def test_rejects_nest_no_name(self): |
298 | self.assertParseError(3, 6, "End of line while looking for " |
299 | "the branch id", self.get_recipe, |
300 | @@ -229,6 +244,42 @@ |
301 | self.assertEqual(None, location) |
302 | self.check_recipe_branch(child_branch, "bar", "http://bar.org") |
303 | |
304 | + def test_builds_recipe_with_nest_part(self): |
305 | + base_branch = self.get_recipe(self.basic_header_and_branch |
306 | + + "nest-part bar http://bar.org some/path") |
307 | + self.check_base_recipe_branch(base_branch, "http://foo.org/", |
308 | + num_child_branches=1) |
309 | + instruction = base_branch.child_branches[0] |
310 | + self.assertEqual(None, instruction.nest_path) |
311 | + self.check_recipe_branch( |
312 | + instruction.recipe_branch, "bar", "http://bar.org") |
313 | + self.assertEqual("some/path", instruction.subpath) |
314 | + self.assertEqual(None, instruction.target_subdir) |
315 | + |
316 | + def test_builds_recipe_with_nest_part_subdir(self): |
317 | + base_branch = self.get_recipe(self.basic_header_and_branch |
318 | + + "nest-part bar http://bar.org some/path target-subdir") |
319 | + self.check_base_recipe_branch(base_branch, "http://foo.org/", |
320 | + num_child_branches=1) |
321 | + instruction = base_branch.child_branches[0] |
322 | + self.assertEqual(None, instruction.nest_path) |
323 | + self.check_recipe_branch( |
324 | + instruction.recipe_branch, "bar", "http://bar.org") |
325 | + self.assertEqual("some/path", instruction.subpath) |
326 | + self.assertEqual("target-subdir", instruction.target_subdir) |
327 | + |
328 | + def test_builds_recipe_with_nest_part_subdir_and_revspec(self): |
329 | + base_branch = self.get_recipe(self.basic_header_and_branch |
330 | + + "nest-part bar http://bar.org some/path target-subdir 1234") |
331 | + self.check_base_recipe_branch(base_branch, "http://foo.org/", |
332 | + num_child_branches=1) |
333 | + instruction = base_branch.child_branches[0] |
334 | + self.assertEqual(None, instruction.nest_path) |
335 | + self.check_recipe_branch( |
336 | + instruction.recipe_branch, "bar", "http://bar.org", "1234") |
337 | + self.assertEqual("some/path", instruction.subpath) |
338 | + self.assertEqual("target-subdir", instruction.target_subdir) |
339 | + |
340 | def test_builds_recipe_with_nest(self): |
341 | base_branch = self.get_recipe(self.basic_header_and_branch |
342 | + "nest bar http://bar.org baz") |
343 | @@ -462,15 +513,17 @@ |
344 | self.assertEqual(revid, tree.last_revision()) |
345 | self.assertEqual(revid, base_branch.revid) |
346 | |
347 | + def make_source_branch(self, relpath): |
348 | + """Make a branch with one file and one commit.""" |
349 | + source1 = self.make_branch_and_tree(relpath) |
350 | + self.build_tree([relpath + "/a"]) |
351 | + source1.add(["a"]) |
352 | + source1.commit("one") |
353 | + return source1 |
354 | + |
355 | def test_build_tree_nested(self): |
356 | - source1 = self.make_branch_and_tree("source1") |
357 | - self.build_tree(["source1/a"]) |
358 | - source1.add(["a"]) |
359 | - source1_rev_id = source1.commit("one") |
360 | - source2 = self.make_branch_and_tree("source2") |
361 | - self.build_tree(["source2/a"]) |
362 | - source2.add(["a"]) |
363 | - source2_rev_id = source2.commit("one") |
364 | + source1_rev_id = self.make_source_branch("source1").last_revision() |
365 | + source2_rev_id = self.make_source_branch("source2").last_revision() |
366 | base_branch = BaseRecipeBranch("source1", "1", 0.2) |
367 | nested_branch = RecipeBranch("nested", "source2") |
368 | base_branch.nest_branch("sub", nested_branch) |
369 | @@ -484,10 +537,8 @@ |
370 | self.assertEqual(source2_rev_id, nested_branch.revid) |
371 | |
372 | def test_build_tree_merged(self): |
373 | - source1 = self.make_branch_and_tree("source1") |
374 | - self.build_tree(["source1/a"]) |
375 | - source1.add(["a"]) |
376 | - source1_rev_id = source1.commit("one") |
377 | + source1 = self.make_source_branch("source1") |
378 | + source1_rev_id = source1.last_revision() |
379 | source2 = source1.bzrdir.sprout("source2").open_workingtree() |
380 | self.build_tree_contents([("source2/a", "other change")]) |
381 | source2_rev_id = source2.commit("one") |
382 | @@ -505,11 +556,60 @@ |
383 | self.assertEqual(source1_rev_id, base_branch.revid) |
384 | self.assertEqual(source2_rev_id, merged_branch.revid) |
385 | |
386 | + def test_build_tree_nest_part(self): |
387 | + """A recipe can specify a merge of just part of an unrelated tree.""" |
388 | + source1 = self.make_source_branch("source1") |
389 | + source2 = self.make_source_branch("source2") |
390 | + source1.lock_read() |
391 | + self.addCleanup(source1.unlock) |
392 | + source1_rev_id = source1.last_revision() |
393 | + # Add 'b' to source2. |
394 | + self.build_tree_contents([ |
395 | + ("source2/b", "new file"), ("source2/not-b", "other file")]) |
396 | + source2.add(["b", "not-b"]) |
397 | + source2_rev_id = source2.commit("two") |
398 | + base_branch = BaseRecipeBranch("source1", "1", 0.2) |
399 | + merged_branch = RecipeBranch("merged", "source2") |
400 | + # Merge just 'b' from source2; 'a' is untouched. |
401 | + base_branch.nest_part_branch(merged_branch, "b") |
402 | + build_tree(base_branch, "target") |
403 | + file_id = source1.path2id("a") |
404 | + self.check_file_contents("target/a", source1.get_file_text(file_id)) |
405 | + self.check_file_contents("target/b", "new file") |
406 | + self.assertNotInWorkingTree("not-b", "target") |
407 | + self.assertEqual(source1_rev_id, base_branch.revid) |
408 | + self.assertEqual(source2_rev_id, merged_branch.revid) |
409 | + |
410 | + def test_build_tree_nest_part_explicit_target(self): |
411 | + """A recipe can specify a merge of just part of an unrelated tree into |
412 | + a specific subdirectory of the target tree. |
413 | + """ |
414 | + source1 = self.make_branch_and_tree("source1") |
415 | + self.build_tree(["source1/dir/"]) |
416 | + source1.add(["dir"]) |
417 | + source1.commit("one") |
418 | + source2 = self.make_source_branch("source2") |
419 | + source1.lock_read() |
420 | + self.addCleanup(source1.unlock) |
421 | + source1_rev_id = source1.last_revision() |
422 | + # Add 'b' to source2. |
423 | + self.build_tree_contents([ |
424 | + ("source2/b", "new file"), ("source2/not-b", "other file")]) |
425 | + source2.add(["b", "not-b"]) |
426 | + source2_rev_id = source2.commit("two") |
427 | + base_branch = BaseRecipeBranch("source1", "1", 0.2) |
428 | + merged_branch = RecipeBranch("merged", "source2") |
429 | + # Merge just 'b' from source2; 'a' is untouched. |
430 | + base_branch.nest_part_branch(merged_branch, "b", "dir/b") |
431 | + build_tree(base_branch, "target") |
432 | + self.check_file_contents("target/dir/b", "new file") |
433 | + self.assertNotInWorkingTree("dir/not-b", "target") |
434 | + self.assertEqual(source1_rev_id, base_branch.revid) |
435 | + self.assertEqual(source2_rev_id, merged_branch.revid) |
436 | + |
437 | def test_build_tree_merge_twice(self): |
438 | - source1 = self.make_branch_and_tree("source1") |
439 | - self.build_tree(["source1/a"]) |
440 | - source1.add(["a"]) |
441 | - source1_rev_id = source1.commit("one") |
442 | + source1 = self.make_source_branch("source1") |
443 | + source1_rev_id = source1.last_revision() |
444 | source2 = source1.bzrdir.sprout("source2").open_workingtree() |
445 | self.build_tree_contents([("source2/a", "other change")]) |
446 | source2_rev_id = source2.commit("one") |
447 | @@ -538,10 +638,7 @@ |
448 | self.assertEqual(source3_rev_id, merged_branch2.revid) |
449 | |
450 | def test_build_tree_merged_with_conflicts(self): |
451 | - source1 = self.make_branch_and_tree("source1") |
452 | - self.build_tree(["source1/a"]) |
453 | - source1.add(["a"]) |
454 | - source1_rev_id = source1.commit("one") |
455 | + source1 = self.make_source_branch("source1") |
456 | source2 = source1.bzrdir.sprout("source2").open_workingtree() |
457 | self.build_tree_contents([("source2/a", "other change\n")]) |
458 | source2_rev_id = source2.commit("one") |
459 | @@ -567,10 +664,8 @@ |
460 | self.assertEqual(source2_rev_id, merged_branch.revid) |
461 | |
462 | def test_build_tree_with_revspecs(self): |
463 | - source1 = self.make_branch_and_tree("source1") |
464 | - self.build_tree(["source1/a"]) |
465 | - source1.add(["a"]) |
466 | - source1_rev_id = source1.commit("one") |
467 | + source1 = self.make_source_branch("source1") |
468 | + source1_rev_id = source1.last_revision() |
469 | source2 = source1.bzrdir.sprout("source2").open_workingtree() |
470 | self.build_tree_contents([("source2/a", "other change\n")]) |
471 | source2_rev_id = source2.commit("one") |
Hi,
The launchpad team have no plans to provide a newer bzr in older
releases, so I would like to proceed by copying the code in to bzr-builder
where needed.
As for the changes proposed here, they are largely fine.
nest-part packaging lp:~foo-dev/foo/packaging -1 debian
I would like to move the revision spec to the end of the line and make it optional.
I don't mind too much whether it or the target subdir comes first.
134 + op = OperationWithCl eanups( nest_part_ branch)
I don't think we can rely on OperationWithCl eanups as it is fairly new,
so either we copy that too, or we do it the old fashioned way.
Thanks for your work on this. Let me know if you would like me to
make these changes.
Thanks,
James