Merge lp:~jelmer/brz/bundle-quilt into lp:brz
- bundle-quilt
- Merge into trunk
Proposed by
Jelmer Vernooij
Status: | Merged |
---|---|
Approved by: | Jelmer Vernooij |
Approved revision: | no longer in the source branch. |
Merge reported by: | The Breezy Bot |
Merged at revision: | not available |
Proposed branch: | lp:~jelmer/brz/bundle-quilt |
Merge into: | lp:brz |
Diff against target: |
1231 lines (+1161/-2) 10 files modified
.travis.yml (+1/-1) breezy/plugins/quilt/__init__.py (+172/-0) breezy/plugins/quilt/merge.py (+146/-0) breezy/plugins/quilt/quilt.py (+91/-0) breezy/plugins/quilt/tests/__init__.py (+39/-0) breezy/plugins/quilt/tests/test_merge.py (+379/-0) breezy/plugins/quilt/tests/test_wrapper.py (+99/-0) breezy/plugins/quilt/wrapper.py (+230/-0) byov.conf (+1/-1) doc/en/release-notes/brz-3.1.txt (+3/-0) |
To merge this branch: | bzr merge lp:~jelmer/brz/bundle-quilt |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Packman | Approve | ||
Review via email: mp+368585@code.launchpad.net |
Commit message
Add a quilt plugin that can do smart things with quilt patches.
Description of the change
Add a quilt plugin that can do smart things with quilt patches.
To post a comment you must log in.
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote : | # |
Running landing tests failed
https:/
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote : | # |
Running landing tests failed
https:/
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.travis.yml' |
2 | --- .travis.yml 2019-05-28 23:22:25 +0000 |
3 | +++ .travis.yml 2019-06-15 13:39:59 +0000 |
4 | @@ -21,7 +21,7 @@ |
5 | - BRZ_PLUGIN_PATH=-site:-user python -Werror -Wignore::ImportWarning -Wignore::PendingDeprecationWarning -Wignore::DeprecationWarning -Wignore::ResourceWarning -Wignore::UserWarning ./brz selftest --parallel=fork $SELFTEST_OPTIONS |
6 | |
7 | install: |
8 | - - sudo apt install python-all-dev python3-all-dev subunit |
9 | + - sudo apt install python-all-dev python3-all-dev subunit quilt |
10 | - travis_retry pip install -U setuptools |
11 | - travis_retry pip install -U pip coverage codecov flake8 testtools paramiko fastimport configobj cython testscenarios six docutils python-subunit $TEST_REQUIRE sphinx sphinx_epytext launchpadlib patiencediff git+https://github.com/dulwich/dulwich |
12 | |
13 | |
14 | === added directory 'breezy/plugins/quilt' |
15 | === added file 'breezy/plugins/quilt/__init__.py' |
16 | --- breezy/plugins/quilt/__init__.py 1970-01-01 00:00:00 +0000 |
17 | +++ breezy/plugins/quilt/__init__.py 2019-06-15 13:39:59 +0000 |
18 | @@ -0,0 +1,172 @@ |
19 | +# __init__.py -- Quilt support for breezy |
20 | +# |
21 | +# Breezy is free software; you can redistribute it and/or modify |
22 | +# it under the terms of the GNU General Public License as published by |
23 | +# the Free Software Foundation; either version 2 of the License, or |
24 | +# (at your option) any later version. |
25 | +# |
26 | +# Breezy is distributed in the hope that it will be useful, |
27 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
28 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
29 | +# GNU General Public License for more details. |
30 | +# |
31 | +# You should have received a copy of the GNU General Public License |
32 | +# along with Breezy; if not, write to the Free Software |
33 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
34 | +# |
35 | + |
36 | +from __future__ import absolute_import |
37 | + |
38 | +"""Quilt patch integration. |
39 | + |
40 | +This plugin adds support for three configuration options: |
41 | + |
42 | + * quilt.commit_policy |
43 | + * quilt.smart_merge |
44 | + * quilt.tree_policy |
45 | + |
46 | +""" |
47 | + |
48 | +from ...errors import BzrError |
49 | +from ... import trace |
50 | + |
51 | + |
52 | +class QuiltUnapplyError(BzrError): |
53 | + |
54 | + _fmt = ("Unable to unapply quilt patches for %(kind)r tree: %(msg)s") |
55 | + |
56 | + def __init__(self, kind, msg): |
57 | + BzrError.__init__(self) |
58 | + self.kind = kind |
59 | + if msg is not None and msg.count("\n") == 1: |
60 | + msg = msg.strip() |
61 | + self.msg = msg |
62 | + |
63 | + |
64 | +def pre_merge_quilt(merger): |
65 | + if getattr(merger, "_no_quilt_unapplying", False): |
66 | + return |
67 | + |
68 | + config = merger.working_tree.get_config_stack() |
69 | + merger.debuild_config = config |
70 | + if not config.get('quilt.smart_merge'): |
71 | + trace.mutter("skipping smart quilt merge, not enabled.") |
72 | + return |
73 | + |
74 | + if (not merger.other_tree.is_versioned(".pc") and |
75 | + not merger.this_tree.is_versioned(".pc") and |
76 | + not merger.working_tree.is_versioned(".pc")): |
77 | + return |
78 | + |
79 | + from .quilt import QuiltPatches, QuiltError |
80 | + quilt = QuiltPatches(merger.working_tree) |
81 | + from .merge import tree_unapply_patches |
82 | + trace.note("Unapplying quilt patches to prevent spurious conflicts") |
83 | + merger._quilt_tempdirs = [] |
84 | + merger._old_quilt_series = quilt.series() |
85 | + if merger._old_quilt_series: |
86 | + quilt.pop_all() |
87 | + try: |
88 | + merger.this_tree, this_dir = tree_unapply_patches( |
89 | + merger.this_tree, merger.this_branch, force=True) |
90 | + except QuiltError as e: |
91 | + raise QuiltUnapplyError("this", e.stderr) |
92 | + else: |
93 | + if this_dir is not None: |
94 | + merger._quilt_tempdirs.append(this_dir) |
95 | + try: |
96 | + merger.base_tree, base_dir = tree_unapply_patches( |
97 | + merger.base_tree, merger.this_branch, force=True) |
98 | + except QuiltError as e: |
99 | + raise QuiltUnapplyError("base", e.stderr) |
100 | + else: |
101 | + if base_dir is not None: |
102 | + merger._quilt_tempdirs.append(base_dir) |
103 | + other_branch = getattr(merger, "other_branch", None) |
104 | + if other_branch is None: |
105 | + other_branch = merger.this_branch |
106 | + try: |
107 | + merger.other_tree, other_dir = tree_unapply_patches( |
108 | + merger.other_tree, other_branch, force=True) |
109 | + except QuiltError as e: |
110 | + raise QuiltUnapplyError("other", e.stderr) |
111 | + else: |
112 | + if other_dir is not None: |
113 | + merger._quilt_tempdirs.append(other_dir) |
114 | + |
115 | + |
116 | +def post_merge_quilt_cleanup(merger): |
117 | + import shutil |
118 | + for dir in getattr(merger, "_quilt_tempdirs", []): |
119 | + shutil.rmtree(dir) |
120 | + config = merger.working_tree.get_config_stack() |
121 | + policy = config.get('quilt.tree_policy') |
122 | + if policy is None: |
123 | + return |
124 | + from .merge import post_process_quilt_patches |
125 | + post_process_quilt_patches( |
126 | + merger.working_tree, |
127 | + getattr(merger, "_old_quilt_series", []), policy) |
128 | + |
129 | + |
130 | +def start_commit_check_quilt(tree): |
131 | + """start_commit hook which checks the state of quilt patches. |
132 | + """ |
133 | + config = tree.get_config_stack() |
134 | + policy = config.get('quilt.commit_policy') |
135 | + from .merge import start_commit_quilt_patches |
136 | + start_commit_quilt_patches(tree, policy) |
137 | + |
138 | + |
139 | +def post_build_tree_quilt(tree): |
140 | + config = tree.get_config_stack() |
141 | + policy = config.get('quilt.tree_policy') |
142 | + if policy is None: |
143 | + return |
144 | + from .merge import post_process_quilt_patches |
145 | + post_process_quilt_patches(tree, [], policy) |
146 | + |
147 | + |
148 | +from ...hooks import install_lazy_named_hook |
149 | +install_lazy_named_hook( |
150 | + "breezy.merge", "Merger.hooks", |
151 | + 'pre_merge_quilt', pre_merge_quilt, |
152 | + 'Quilt patch (un)applying') |
153 | +install_lazy_named_hook( |
154 | + "breezy.mutabletree", "MutableTree.hooks", |
155 | + "start_commit", start_commit_check_quilt, |
156 | + "Check for (un)applied quilt patches") |
157 | +install_lazy_named_hook( |
158 | + "breezy.merge", "Merger.hooks", |
159 | + 'post_merge', post_merge_quilt_cleanup, |
160 | + 'Cleaning up quilt temporary directories') |
161 | +install_lazy_named_hook( |
162 | + "breezy.mutabletree", "MutableTree.hooks", |
163 | + 'post_build_tree', post_build_tree_quilt, |
164 | + 'Applying quilt patches.') |
165 | + |
166 | + |
167 | +from ...config import option_registry, Option, bool_from_store |
168 | +option_registry.register( |
169 | + Option('quilt.smart_merge', default=True, from_unicode=bool_from_store, |
170 | + help="Unapply quilt patches before merging.")) |
171 | + |
172 | + |
173 | +def policy_from_store(s): |
174 | + if s not in ('applied', 'unapplied'): |
175 | + raise ValueError('Invalid quilt.commit_policy: %s' % s) |
176 | + return s |
177 | + |
178 | +option_registry.register( |
179 | + Option('quilt.commit_policy', default=None, from_unicode=policy_from_store, |
180 | + help="Whether to apply or unapply all patches in commits.")) |
181 | + |
182 | +option_registry.register( |
183 | + Option('quilt.tree_policy', default=None, from_unicode=policy_from_store, |
184 | + help="Whether to apply or unapply all patches after checkout/update.")) |
185 | + |
186 | + |
187 | +def load_tests(loader, basic_tests, pattern): |
188 | + basic_tests.addTest( |
189 | + loader.loadTestsFromModuleNames([__name__ + '.tests'])) |
190 | + return basic_tests |
191 | |
192 | === added file 'breezy/plugins/quilt/merge.py' |
193 | --- breezy/plugins/quilt/merge.py 1970-01-01 00:00:00 +0000 |
194 | +++ breezy/plugins/quilt/merge.py 2019-06-15 13:39:59 +0000 |
195 | @@ -0,0 +1,146 @@ |
196 | +# quilt.py -- Quilt patch handling |
197 | +# Copyright (C) 2011 Canonical Ltd. |
198 | +# Copyright (C) 2019 Jelmer Verooij <jelmer@jelmer.uk> |
199 | +# |
200 | +# Breezy is free software; you can redistribute it and/or modify |
201 | +# it under the terms of the GNU General Public License as published by |
202 | +# the Free Software Foundation; either version 2 of the License, or |
203 | +# (at your option) any later version. |
204 | +# |
205 | +# Breezy is distributed in the hope that it will be useful, |
206 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
207 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
208 | +# GNU General Public License for more details. |
209 | +# |
210 | +# You should have received a copy of the GNU General Public License |
211 | +# along with Breezy; if not, write to the Free Software |
212 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
213 | +# |
214 | + |
215 | +"""Quilt patch handling.""" |
216 | + |
217 | +from __future__ import absolute_import |
218 | + |
219 | +import shutil |
220 | +import tempfile |
221 | + |
222 | +from ...i18n import gettext |
223 | +from ...mutabletree import MutableTree |
224 | +from ...revisiontree import RevisionTree |
225 | +from ... import ( |
226 | + errors, |
227 | + merge as _mod_merge, |
228 | + trace, |
229 | + ) |
230 | + |
231 | +from .quilt import ( |
232 | + QuiltPatches, |
233 | +) |
234 | + |
235 | + |
236 | +class NoUnapplyingMerger(_mod_merge.Merge3Merger): |
237 | + |
238 | + _no_quilt_unapplying = True |
239 | + |
240 | + |
241 | +def tree_unapply_patches(orig_tree, orig_branch=None, force=False): |
242 | + """Return a tree with patches unapplied. |
243 | + |
244 | + :param orig_tree: Tree from which to unapply quilt patches |
245 | + :param orig_branch: Related branch (optional) |
246 | + :return: Tuple with tree and temp path. |
247 | + The tree is a tree with unapplied patches; either a checkout of |
248 | + tree or tree itself if there were no patches |
249 | + """ |
250 | + if orig_branch is None: |
251 | + orig_branch = orig_tree.branch |
252 | + quilt = QuiltPatches.find(orig_tree) |
253 | + if quilt is None: |
254 | + return orig_tree, None |
255 | + applied_patches = quilt.applied() |
256 | + if not applied_patches: |
257 | + # No quilt patches |
258 | + return orig_tree, None |
259 | + |
260 | + target_dir = tempfile.mkdtemp() |
261 | + try: |
262 | + if isinstance(orig_tree, MutableTree): |
263 | + tree = orig_branch.create_checkout( |
264 | + target_dir, lightweight=True, |
265 | + revision_id=orig_tree.last_revision(), |
266 | + accelerator_tree=orig_tree) |
267 | + merger = _mod_merge.Merger.from_uncommitted(tree, orig_tree) |
268 | + merger.merge_type = NoUnapplyingMerger |
269 | + merger.do_merge() |
270 | + elif isinstance(orig_tree, RevisionTree): |
271 | + tree = orig_branch.create_checkout( |
272 | + target_dir, lightweight=True, |
273 | + accelerator_tree=orig_tree, revision_id=orig_tree.get_revision_id()) |
274 | + else: |
275 | + trace.mutter("Not sure how to create copy of %r", orig_tree) |
276 | + shutil.rmtree(target_dir) |
277 | + return orig_tree, None |
278 | + trace.mutter("Applying quilt patches for %r in %s", orig_tree, target_dir) |
279 | + quilt = QuiltPatches.find(tree) |
280 | + if quilt is not None: |
281 | + quilt.pop_all(force=force) |
282 | + return tree, target_dir |
283 | + except BaseException: |
284 | + shutil.rmtree(target_dir) |
285 | + raise |
286 | + |
287 | + |
288 | +def post_process_quilt_patches(tree, old_patches, policy): |
289 | + """(Un)apply patches after a merge. |
290 | + |
291 | + :param tree: Working tree to work in |
292 | + :param old_patches: List of patches applied before the operation (usually a merge) |
293 | + """ |
294 | + quilt = QuiltPatches.find(tree) |
295 | + if quilt is None: |
296 | + return |
297 | + new_patches = quilt.series() |
298 | + applied_patches = quilt.applied() |
299 | + if policy == "applied": |
300 | + to_apply = [] |
301 | + for p in new_patches: |
302 | + if p in old_patches: |
303 | + continue |
304 | + if p not in applied_patches: |
305 | + to_apply.append(p) |
306 | + if to_apply == []: |
307 | + return |
308 | + trace.note(gettext("Applying %d quilt patches."), len(to_apply)) |
309 | + for p in to_apply: |
310 | + quilt.push(p) |
311 | + elif policy == "unapplied": |
312 | + to_unapply = [] |
313 | + for p in new_patches: |
314 | + if p in old_patches: |
315 | + continue |
316 | + if p in applied_patches: |
317 | + to_unapply.append(p) |
318 | + if to_unapply == []: |
319 | + return |
320 | + trace.note(gettext("Unapplying %d quilt patches."), len(to_unapply)) |
321 | + for p in to_unapply: |
322 | + quilt.pop(p) |
323 | + |
324 | + |
325 | +def start_commit_quilt_patches(tree, policy): |
326 | + quilt = QuiltPatches.find(tree) |
327 | + if quilt is None: |
328 | + return |
329 | + applied_patches = quilt.applied() |
330 | + unapplied_patches = quilt.unapplied() |
331 | + if policy is None: |
332 | + # No policy set - just warn about having both applied and unapplied |
333 | + # patches. |
334 | + if applied_patches and unapplied_patches: |
335 | + trace.warning( |
336 | + gettext("Committing with %d patches applied and %d patches unapplied."), |
337 | + len(applied_patches), len(unapplied_patches)) |
338 | + elif policy == "applied": |
339 | + quilt.push_all() |
340 | + elif policy == "unapplied": |
341 | + quilt.pop_all() |
342 | |
343 | === added file 'breezy/plugins/quilt/quilt.py' |
344 | --- breezy/plugins/quilt/quilt.py 1970-01-01 00:00:00 +0000 |
345 | +++ breezy/plugins/quilt/quilt.py 2019-06-15 13:39:59 +0000 |
346 | @@ -0,0 +1,91 @@ |
347 | +# quilt.py -- Quilt patch handling |
348 | +# Copyright (C) 2019 Jelmer Vernooij <jelmer@jelmer.uk> |
349 | +# |
350 | +# Breezy is free software; you can redistribute it and/or modify |
351 | +# it under the terms of the GNU General Public License as published by |
352 | +# the Free Software Foundation; either version 2 of the License, or |
353 | +# (at your option) any later version. |
354 | +# |
355 | +# Breezy is distributed in the hope that it will be useful, |
356 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
357 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
358 | +# GNU General Public License for more details. |
359 | +# |
360 | +# You should have received a copy of the GNU General Public License |
361 | +# along with Breezy; if not, write to the Free Software |
362 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
363 | +# |
364 | + |
365 | +"""Quilt patch handling.""" |
366 | + |
367 | +from __future__ import absolute_import |
368 | + |
369 | +import os |
370 | + |
371 | +from ... import osutils |
372 | +from . import wrapper |
373 | + |
374 | +QuiltError = wrapper.QuiltError |
375 | + |
376 | +DEFAULT_PATCHES_DIR = 'patches' |
377 | + |
378 | + |
379 | +class QuiltPatches(object): |
380 | + """Management object for a stack of quilt patches.""" |
381 | + |
382 | + def __init__(self, tree, patches_dir=None, series_file=None): |
383 | + self.tree = tree |
384 | + if patches_dir is None: |
385 | + if tree.has_filename('.pc/.quilt_patches'): |
386 | + patches_dir = tree.get_file_text('.pc/.quilt_patches').decode( |
387 | + osutils._fs_enc) |
388 | + else: |
389 | + patches_dir = DEFAULT_PATCHES_DIR |
390 | + self.patches_dir = patches_dir |
391 | + if series_file is None: |
392 | + series_file = os.path.join(patches_dir, 'series') |
393 | + self.series_file = series_file |
394 | + |
395 | + @classmethod |
396 | + def find(cls, tree): |
397 | + if tree.has_filename('.pc/.quilt_patches'): |
398 | + return cls(tree) |
399 | + for name in ['patches', 'debian/patches']: |
400 | + if tree.has_filename(name): |
401 | + return cls(tree, name) |
402 | + return None |
403 | + |
404 | + def upgrade(self): |
405 | + return wrapper.quilt_upgrade(self.tree.basedir) |
406 | + |
407 | + def series(self): |
408 | + return wrapper.quilt_series(self.tree, self.series_file) |
409 | + |
410 | + def applied(self): |
411 | + return wrapper.quilt_applied(self.tree) |
412 | + |
413 | + def unapplied(self): |
414 | + return wrapper.quilt_unapplied( |
415 | + self.tree.basedir, self.patches_dir, self.series_file) |
416 | + |
417 | + def pop_all(self, quiet=None, force=False, refresh=False): |
418 | + return wrapper.quilt_pop_all( |
419 | + self.tree.basedir, patches_dir=self.patches_dir, |
420 | + series_file=self.series_file, quiet=quiet, force=force, |
421 | + refresh=refresh) |
422 | + |
423 | + def push_all(self, quiet=None, force=None, refresh=None): |
424 | + return wrapper.quilt_push_all( |
425 | + self.tree.basedir, patches_dir=self.patches_dir, |
426 | + series_file=self.series_file, quiet=quiet, force=force, |
427 | + refresh=refresh) |
428 | + |
429 | + def push(self, patch, quiet=None): |
430 | + return wrapper.quilt_push( |
431 | + self.tree.basedir, patch, patches_dir=self.patches_dir, |
432 | + series_file=self.series_file, quiet=quiet) |
433 | + |
434 | + def pop(self, patch, quiet=None): |
435 | + return wrapper.quilt_pop( |
436 | + self.tree.basedir, patch, patches_dir=self.patches_dir, |
437 | + series_file=self.series_file, quiet=quiet) |
438 | |
439 | === added directory 'breezy/plugins/quilt/tests' |
440 | === added file 'breezy/plugins/quilt/tests/__init__.py' |
441 | --- breezy/plugins/quilt/tests/__init__.py 1970-01-01 00:00:00 +0000 |
442 | +++ breezy/plugins/quilt/tests/__init__.py 2019-06-15 13:39:59 +0000 |
443 | @@ -0,0 +1,39 @@ |
444 | +# __init__.py -- Testsuite for quilt |
445 | +# Copyright (C) 2019 Jelmer Vernooij <jelmer@jelmer.uk> |
446 | +# |
447 | +# Breezy is free software; you can redistribute it and/or modify |
448 | +# it under the terms of the GNU General Public License as published by |
449 | +# the Free Software Foundation; either version 2 of the License, or |
450 | +# (at your option) any later version. |
451 | +# |
452 | +# Breezy is distributed in the hope that it will be useful, |
453 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
454 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
455 | +# GNU General Public License for more details. |
456 | +# |
457 | +# You should have received a copy of the GNU General Public License |
458 | +# along with Breezy; if not, write to the Free Software |
459 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
460 | +# |
461 | + |
462 | +from __future__ import absolute_import |
463 | + |
464 | +from .... import tests |
465 | + |
466 | +from ....tests import ( |
467 | + TestUtil, |
468 | + multiply_tests, |
469 | + TestCaseWithTransport, |
470 | + TestCaseInTempDir, |
471 | + ) |
472 | + |
473 | + |
474 | +def load_tests(loader, basic_tests, pattern): |
475 | + testmod_names = [ |
476 | + 'test_merge', |
477 | + 'test_wrapper', |
478 | + ] |
479 | + basic_tests.addTest(loader.loadTestsFromModuleNames( |
480 | + ["%s.%s" % (__name__, i) for i in testmod_names])) |
481 | + |
482 | + return basic_tests |
483 | |
484 | === added file 'breezy/plugins/quilt/tests/test_merge.py' |
485 | --- breezy/plugins/quilt/tests/test_merge.py 1970-01-01 00:00:00 +0000 |
486 | +++ breezy/plugins/quilt/tests/test_merge.py 2019-06-15 13:39:59 +0000 |
487 | @@ -0,0 +1,379 @@ |
488 | +# Copyright (C) 2011 Canonical Ltd |
489 | +# Copyright (C) 2019 Jelmer Vernooij <jelmer@jelmer.uk> |
490 | +# |
491 | +# Breezy is free software; you can redistribute it and/or modify |
492 | +# it under the terms of the GNU General Public License as published by |
493 | +# the Free Software Foundation; either version 2 of the License, or |
494 | +# (at your option) any later version. |
495 | +# |
496 | +# Breezy is distributed in the hope that it will be useful, |
497 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
498 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
499 | +# GNU General Public License for more details. |
500 | +# |
501 | +# You should have received a copy of the GNU General Public License |
502 | +# along with Breezy; if not, write to the Free Software |
503 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
504 | +# |
505 | + |
506 | +"""Tests for the merge_quilt code.""" |
507 | + |
508 | +from __future__ import absolute_import |
509 | + |
510 | +import os |
511 | +import shutil |
512 | + |
513 | +from .... import ( |
514 | + config, |
515 | + errors, |
516 | + trace, |
517 | + ) |
518 | +from ....merge import Merger |
519 | +from ....mutabletree import MutableTree |
520 | + |
521 | +from .. import ( |
522 | + pre_merge_quilt, |
523 | + start_commit_check_quilt, |
524 | + post_build_tree_quilt, |
525 | + post_merge_quilt_cleanup, |
526 | + ) |
527 | +from ..quilt import QuiltPatches |
528 | +from ..merge import tree_unapply_patches |
529 | + |
530 | +from .test_wrapper import quilt_feature |
531 | + |
532 | +from ....tests import ( |
533 | + TestCaseWithTransport, |
534 | + TestSkipped, |
535 | + ) |
536 | + |
537 | +TRIVIAL_PATCH = """--- /dev/null 2012-01-02 01:09:10.986490031 +0100 |
538 | ++++ base/a 2012-01-02 20:03:59.710666215 +0100 |
539 | +@@ -0,0 +1 @@ |
540 | ++a |
541 | +""" |
542 | + |
543 | + |
544 | +def quilt_push_all(tree): |
545 | + QuiltPatches(tree, 'debian/patches').push_all() |
546 | + |
547 | + |
548 | +class TestTreeUnapplyPatches(TestCaseWithTransport): |
549 | + |
550 | + _test_needs_features = [quilt_feature] |
551 | + |
552 | + def test_no_patches(self): |
553 | + tree = self.make_branch_and_tree('.') |
554 | + new_tree, target_dir = tree_unapply_patches(tree) |
555 | + self.assertIs(tree, new_tree) |
556 | + self.assertIs(None, target_dir) |
557 | + |
558 | + def test_unapply(self): |
559 | + orig_tree = self.make_branch_and_tree('source') |
560 | + self.build_tree(["source/debian/", "source/debian/patches/"]) |
561 | + self.build_tree_contents([ |
562 | + ("source/debian/patches/series", "patch1.diff\n"), |
563 | + ("source/debian/patches/patch1.diff", TRIVIAL_PATCH)]) |
564 | + quilt_push_all(orig_tree) |
565 | + orig_tree.smart_add([orig_tree.basedir]) |
566 | + tree, target_dir = tree_unapply_patches(orig_tree) |
567 | + self.addCleanup(shutil.rmtree, target_dir) |
568 | + self.assertPathExists("source/a") |
569 | + self.assertNotEqual(tree.basedir, orig_tree.basedir) |
570 | + self.assertPathDoesNotExist(tree.abspath("a")) |
571 | + self.assertPathExists(tree.abspath("debian/patches/series")) |
572 | + |
573 | + def test_unapply_nothing_applied(self): |
574 | + orig_tree = self.make_branch_and_tree('source') |
575 | + self.build_tree(["source/debian/", "source/debian/patches/"]) |
576 | + self.build_tree_contents([ |
577 | + ("source/debian/patches/series", "patch1.diff\n"), |
578 | + ("source/debian/patches/patch1.diff", TRIVIAL_PATCH)]) |
579 | + orig_tree.smart_add([orig_tree.basedir]) |
580 | + tree, target_dir = tree_unapply_patches(orig_tree) |
581 | + self.assertIs(tree, orig_tree) |
582 | + self.assertIs(None, target_dir) |
583 | + |
584 | + |
585 | +class TestMergeHook(TestCaseWithTransport): |
586 | + |
587 | + _test_needs_features = [quilt_feature] |
588 | + |
589 | + def enable_hooks(self): |
590 | + Merger.hooks.install_named_hook( |
591 | + 'pre_merge', pre_merge_quilt, |
592 | + 'Debian quilt patch (un)applying and ancestry fixing') |
593 | + Merger.hooks.install_named_hook( |
594 | + 'post_merge', post_merge_quilt_cleanup, |
595 | + 'Cleaning up quilt temporary directories') |
596 | + MutableTree.hooks.install_named_hook( |
597 | + "post_build_tree", post_build_tree_quilt, |
598 | + "Apply quilt trees.") |
599 | + |
600 | + def test_diverged_patches(self): |
601 | + self.enable_hooks() |
602 | + |
603 | + tree_a = self.make_branch_and_tree('a') |
604 | + self.build_tree( |
605 | + ['a/debian/', 'a/debian/patches/', 'a/debian/source/', 'a/.pc/']) |
606 | + self.build_tree_contents([ |
607 | + ('a/.pc/.quilt_patches', 'debian/patches'), |
608 | + ('a/.pc/.version', '2'), |
609 | + ('a/debian/source/format', '3.0 (quilt)'), |
610 | + ('a/debian/patches/series', 'patch1\n'), |
611 | + ('a/debian/patches/patch1', TRIVIAL_PATCH)]) |
612 | + tree_a.smart_add([tree_a.basedir]) |
613 | + tree_a.commit('initial') |
614 | + |
615 | + tree_b = tree_a.controldir.sprout('b').open_workingtree() |
616 | + self.build_tree_contents([ |
617 | + ('a/debian/patches/patch1', |
618 | + "\n".join(TRIVIAL_PATCH.splitlines()[:-1] + ["+d\n"]))]) |
619 | + quilt_push_all(tree_a) |
620 | + tree_a.smart_add([tree_a.basedir]) |
621 | + tree_a.commit('apply patches') |
622 | + self.build_tree_contents([ |
623 | + ('b/debian/patches/patch1', |
624 | + "\n".join(TRIVIAL_PATCH.splitlines()[:-1] + ["+c\n"]))]) |
625 | + quilt_push_all(tree_b) |
626 | + tree_b.commit('apply patches') |
627 | + conflicts = tree_a.merge_from_branch(tree_b.branch) |
628 | + self.assertFileEqual("""\ |
629 | +--- /dev/null\t2012-01-02 01:09:10.986490031 +0100 |
630 | ++++ base/a\t2012-01-02 20:03:59.710666215 +0100 |
631 | +@@ -0,0 +1 @@ |
632 | +<<<<<<< TREE |
633 | ++d |
634 | +======= |
635 | ++c |
636 | +>>>>>>> MERGE-SOURCE |
637 | +""", "a/debian/patches/patch1") |
638 | + # "a" should be unapplied again |
639 | + self.assertPathDoesNotExist("a/a") |
640 | + self.assertEquals(1, conflicts) |
641 | + |
642 | + def test_auto_apply_patches_after_checkout(self): |
643 | + self.enable_hooks() |
644 | + |
645 | + tree_a = self.make_branch_and_tree('a') |
646 | + |
647 | + self.build_tree(['a/debian/', 'a/debian/patches/']) |
648 | + self.build_tree_contents([ |
649 | + ('a/debian/patches/series', 'patch1\n'), |
650 | + ('a/debian/patches/patch1', TRIVIAL_PATCH)]) |
651 | + tree_a.smart_add([tree_a.basedir]) |
652 | + tree_a.commit('initial') |
653 | + |
654 | + config.ensure_config_dir_exists() |
655 | + config.GlobalStack().set('quilt.tree_policy', 'applied') |
656 | + |
657 | + tree_a.branch.create_checkout("b") |
658 | + self.assertFileEqual("a\n", "b/a") |
659 | + |
660 | + def test_auto_apply_patches_after_update_format_1(self): |
661 | + self.enable_hooks() |
662 | + |
663 | + tree_a = self.make_branch_and_tree('a') |
664 | + tree_b = tree_a.branch.create_checkout("b") |
665 | + |
666 | + self.build_tree(['a/debian/', 'a/debian/patches/', 'a/.pc/']) |
667 | + self.build_tree_contents([ |
668 | + ('a/.pc/.quilt_patches', 'debian/patches'), |
669 | + ('a/.pc/.version', '2'), |
670 | + ('a/debian/patches/series', 'patch1\n'), |
671 | + ('a/debian/patches/patch1', TRIVIAL_PATCH)]) |
672 | + tree_a.smart_add([tree_a.basedir]) |
673 | + tree_a.commit('initial') |
674 | + |
675 | + self.build_tree(["b/.bzr-builddeb/", "b/debian/", "b/debian/source/"]) |
676 | + tree_b.get_config_stack().set('quilt.tree_policy', 'applied') |
677 | + self.build_tree_contents([ |
678 | + ("b/debian/source/format", "1.0")]) |
679 | + |
680 | + tree_b.update() |
681 | + self.assertFileEqual("a\n", "b/a") |
682 | + |
683 | + def test_auto_apply_patches_after_update(self): |
684 | + self.enable_hooks() |
685 | + |
686 | + tree_a = self.make_branch_and_tree('a') |
687 | + tree_b = tree_a.branch.create_checkout("b") |
688 | + |
689 | + self.build_tree(['a/debian/', 'a/debian/patches/', 'a/debian/source/', 'a/.pc/']) |
690 | + self.build_tree_contents([ |
691 | + ('a/.pc/.quilt_patches', 'debian/patches'), |
692 | + ('a/.pc/.version', '2'), |
693 | + ('a/debian/source/format', '3.0 (quilt)'), |
694 | + ('a/debian/patches/series', 'patch1\n'), |
695 | + ('a/debian/patches/patch1', TRIVIAL_PATCH)]) |
696 | + tree_a.smart_add([tree_a.basedir]) |
697 | + tree_a.commit('initial') |
698 | + |
699 | + self.build_tree(["b/.bzr-builddeb/", "b/debian/", "b/debian/source/"]) |
700 | + tree_b.get_config_stack().set('quilt.tree_policy', 'applied') |
701 | + self.build_tree_contents([ |
702 | + ('b/debian/source/format', '3.0 (quilt)'), |
703 | + ]) |
704 | + |
705 | + tree_b.update() |
706 | + self.assertFileEqual("a\n", "b/a") |
707 | + |
708 | + def test_auto_unapply_patches_after_update(self): |
709 | + self.enable_hooks() |
710 | + |
711 | + tree_a = self.make_branch_and_tree('a') |
712 | + tree_b = tree_a.branch.create_checkout("b") |
713 | + |
714 | + self.build_tree(['a/debian/', 'a/debian/patches/', 'a/debian/source/', 'a/.pc/']) |
715 | + self.build_tree_contents([ |
716 | + ('a/.pc/.quilt_patches', 'debian/patches'), |
717 | + ('a/.pc/.version', '2'), |
718 | + ('a/debian/source/format', '3.0 (quilt)'), |
719 | + ('a/debian/patches/series', 'patch1\n'), |
720 | + ('a/debian/patches/patch1', TRIVIAL_PATCH)]) |
721 | + tree_a.smart_add([tree_a.basedir]) |
722 | + tree_a.commit('initial') |
723 | + |
724 | + self.build_tree(["b/.bzr-builddeb/"]) |
725 | + tree_b.get_config_stack().set('quilt.tree_policy', 'unapplied') |
726 | + |
727 | + tree_b.update() |
728 | + self.assertPathDoesNotExist("b/a") |
729 | + |
730 | + def test_disabled_hook(self): |
731 | + self.enable_hooks() |
732 | + |
733 | + tree_a = self.make_branch_and_tree('a') |
734 | + tree_a.get_config_stack().set('quilt.smart_merge', False) |
735 | + self.build_tree(['a/debian/', 'a/debian/patches/', 'a/.pc/']) |
736 | + self.build_tree_contents([ |
737 | + ('a/.pc/.quilt_patches', 'debian/patches'), |
738 | + ('a/.pc/.version', '2'), |
739 | + ('a/debian/patches/series', 'patch1\n'), |
740 | + ('a/debian/patches/patch1', TRIVIAL_PATCH), |
741 | + ("a/a", "")]) |
742 | + tree_a.smart_add([tree_a.basedir]) |
743 | + tree_a.commit('initial') |
744 | + |
745 | + tree_b = tree_a.controldir.sprout('b').open_workingtree() |
746 | + self.build_tree_contents([ |
747 | + ('a/debian/patches/patch1', |
748 | + "\n".join(TRIVIAL_PATCH.splitlines()[:-1] + ["+d\n"]))]) |
749 | + quilt_push_all(tree_a) |
750 | + tree_a.smart_add([tree_a.basedir]) |
751 | + tree_a.commit('apply patches') |
752 | + self.assertFileEqual("d\n", "a/a") |
753 | + self.build_tree_contents([ |
754 | + ('b/debian/patches/patch1', |
755 | + "\n".join(TRIVIAL_PATCH.splitlines()[:-1] + ["+c\n"]))]) |
756 | + quilt_push_all(tree_b) |
757 | + tree_b.commit('apply patches') |
758 | + self.assertFileEqual("c\n", "b/a") |
759 | + conflicts = tree_a.merge_from_branch(tree_b.branch) |
760 | + self.assertFileEqual("""\ |
761 | +--- /dev/null\t2012-01-02 01:09:10.986490031 +0100 |
762 | ++++ base/a\t2012-01-02 20:03:59.710666215 +0100 |
763 | +@@ -0,0 +1 @@ |
764 | +<<<<<<< TREE |
765 | ++d |
766 | +======= |
767 | ++c |
768 | +>>>>>>> MERGE-SOURCE |
769 | +""", "a/debian/patches/patch1") |
770 | + self.assertFileEqual("""\ |
771 | +<<<<<<< TREE |
772 | +d |
773 | +======= |
774 | +c |
775 | +>>>>>>> MERGE-SOURCE |
776 | +""", "a/a") |
777 | + self.assertEquals(2, conflicts) |
778 | + |
779 | + |
780 | + |
781 | +class StartCommitMergeHookTests(TestCaseWithTransport): |
782 | + |
783 | + _test_needs_features = [quilt_feature] |
784 | + |
785 | + def enable_hooks(self): |
786 | + MutableTree.hooks.install_named_hook( |
787 | + 'start_commit', start_commit_check_quilt, |
788 | + 'Check for (un)applied quilt patches') |
789 | + |
790 | + def test_applied(self): |
791 | + self.enable_hooks() |
792 | + tree = self.make_branch_and_tree('source') |
793 | + tree.get_config_stack().set('quilt.commit_policy', 'applied') |
794 | + self.build_tree(['source/debian/', 'source/debian/patches/', |
795 | + 'source/debian/source/']) |
796 | + self.build_tree_contents([ |
797 | + ('source/debian/source/format', '3.0 (quilt)'), |
798 | + ('source/debian/patches/series', 'patch1\n'), |
799 | + ('source/debian/patches/patch1', TRIVIAL_PATCH)]) |
800 | + self.assertPathDoesNotExist("source/.pc/applied-patches") |
801 | + self.assertPathDoesNotExist("source/a") |
802 | + tree.smart_add([tree.basedir]) |
803 | + tree.commit("foo") |
804 | + self.assertPathExists("source/.pc/applied-patches") |
805 | + self.assertPathExists("source/a") |
806 | + |
807 | + def test_unapplied(self): |
808 | + self.enable_hooks() |
809 | + tree = self.make_branch_and_tree('source') |
810 | + tree.get_config_stack().set('quilt.commit_policy', 'unapplied') |
811 | + self.build_tree( |
812 | + ['source/debian/', 'source/debian/patches/', |
813 | + 'source/debian/source/']) |
814 | + self.build_tree_contents([ |
815 | + ('source/debian/patches/series', 'patch1\n'), |
816 | + ('source/debian/patches/patch1', TRIVIAL_PATCH), |
817 | + ('source/debian/source/format', '3.0 (quilt)')]) |
818 | + quilt_push_all(tree) |
819 | + self.assertPathExists("source/.pc/applied-patches") |
820 | + self.assertPathExists("source/a") |
821 | + tree.smart_add([tree.basedir]) |
822 | + tree.commit("foo") |
823 | + self.assertPathDoesNotExist("source/.pc/applied-patches") |
824 | + self.assertPathDoesNotExist("source/a") |
825 | + |
826 | + def test_warning(self): |
827 | + self.enable_hooks() |
828 | + warnings = [] |
829 | + |
830 | + def warning(*args): |
831 | + if len(args) > 1: |
832 | + warnings.append(args[0] % args[1:]) |
833 | + else: |
834 | + warnings.append(args[0]) |
835 | + _warning = trace.warning |
836 | + trace.warning = warning |
837 | + self.addCleanup(setattr, trace, "warning", _warning) |
838 | + tree = self.make_branch_and_tree('source') |
839 | + self.build_tree(['source/debian/', 'source/debian/patches/', |
840 | + 'source/debian/source/']) |
841 | + self.build_tree_contents([ |
842 | + ('source/debian/patches/series', 'patch1\n'), |
843 | + ('source/debian/patches/patch1', TRIVIAL_PATCH)]) |
844 | + quilt_push_all(tree) |
845 | + tree.smart_add([tree.basedir]) |
846 | + tree.commit("initial") |
847 | + self.assertEquals([], warnings) |
848 | + self.assertPathExists("source/.pc/applied-patches") |
849 | + self.assertPathExists("source/a") |
850 | + self.build_tree_contents([ |
851 | + ('source/debian/source/format', '3.0 (quilt)'), |
852 | + ('source/debian/patches/series', 'patch1\npatch2\n'), |
853 | + ('source/debian/patches/patch2', |
854 | + """--- /dev/null 2012-01-02 01:09:10.986490031 +0100 |
855 | ++++ base/b 2012-01-02 20:03:59.710666215 +0100 |
856 | +@@ -0,0 +1 @@ |
857 | ++a |
858 | +""")]) |
859 | + tree.smart_add([tree.basedir]) |
860 | + tree.commit("foo") |
861 | + self.assertEquals( |
862 | + ['Committing with 1 patches applied and 1 patches unapplied.'], |
863 | + warnings) |
864 | + self.assertPathExists("source/.pc/applied-patches") |
865 | + self.assertPathExists("source/a") |
866 | + self.assertPathDoesNotExist("source/b") |
867 | |
868 | === added file 'breezy/plugins/quilt/tests/test_wrapper.py' |
869 | --- breezy/plugins/quilt/tests/test_wrapper.py 1970-01-01 00:00:00 +0000 |
870 | +++ breezy/plugins/quilt/tests/test_wrapper.py 2019-06-15 13:39:59 +0000 |
871 | @@ -0,0 +1,99 @@ |
872 | +# Copyright (C) 2011 Canonical Ltd |
873 | +# Copyright (C) 2019 Jelmer Vernooij <jelmer@jelmer.uk> |
874 | +# |
875 | +# Breezy is free software; you can redistribute it and/or modify |
876 | +# it under the terms of the GNU General Public License as published by |
877 | +# the Free Software Foundation; either version 2 of the License, or |
878 | +# (at your option) any later version. |
879 | +# |
880 | +# Breezy is distributed in the hope that it will be useful, |
881 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
882 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
883 | +# GNU General Public License for more details. |
884 | +# |
885 | +# You should have received a copy of the GNU General Public License |
886 | +# along with Breezy; if not, write to the Free Software |
887 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
888 | +# |
889 | + |
890 | +"""Tests for the quilt code.""" |
891 | + |
892 | +from __future__ import absolute_import |
893 | + |
894 | +import os |
895 | + |
896 | +from ..wrapper import ( |
897 | + quilt_pop_all, |
898 | + quilt_applied, |
899 | + quilt_unapplied, |
900 | + quilt_push_all, |
901 | + quilt_series, |
902 | + ) |
903 | + |
904 | +from ....tests import TestCaseWithTransport |
905 | +from ....tests.features import ExecutableFeature |
906 | + |
907 | +quilt_feature = ExecutableFeature('quilt') |
908 | + |
909 | +TRIVIAL_PATCH = """--- /dev/null 2012-01-02 01:09:10.986490031 +0100 |
910 | ++++ base/a 2012-01-02 20:03:59.710666215 +0100 |
911 | +@@ -0,0 +1 @@ |
912 | ++a |
913 | +""" |
914 | + |
915 | +class QuiltTests(TestCaseWithTransport): |
916 | + |
917 | + _test_needs_features = [quilt_feature] |
918 | + |
919 | + def make_empty_quilt_dir(self, path): |
920 | + source = self.make_branch_and_tree(path) |
921 | + self.build_tree( |
922 | + [os.path.join(path, n) for n in ['patches/']]) |
923 | + self.build_tree_contents([ |
924 | + (os.path.join(path, "patches/series"), "\n")]) |
925 | + source.add(["patches", "patches/series"]) |
926 | + return source |
927 | + |
928 | + def test_series_all_empty(self): |
929 | + source = self.make_empty_quilt_dir("source") |
930 | + self.assertEquals([], quilt_series(source, 'patches/series')) |
931 | + |
932 | + def test_series_all(self): |
933 | + source = self.make_empty_quilt_dir("source") |
934 | + self.build_tree_contents([ |
935 | + ("source/patches/series", "patch1.diff\n"), |
936 | + ("source/patches/patch1.diff", TRIVIAL_PATCH)]) |
937 | + source.smart_add(["source"]) |
938 | + self.assertEquals( |
939 | + ["patch1.diff"], quilt_series(source, 'patches/series')) |
940 | + |
941 | + def test_push_all_empty(self): |
942 | + self.make_empty_quilt_dir("source") |
943 | + quilt_push_all("source", quiet=True) |
944 | + |
945 | + def test_poph_all_empty(self): |
946 | + self.make_empty_quilt_dir("source") |
947 | + quilt_pop_all("source", quiet=True) |
948 | + |
949 | + def test_applied_empty(self): |
950 | + source = self.make_empty_quilt_dir("source") |
951 | + self.build_tree_contents([ |
952 | + ("source/patches/series", "patch1.diff\n"), |
953 | + ("source/patches/patch1.diff", "foob ar")]) |
954 | + self.assertEquals([], quilt_applied(source)) |
955 | + |
956 | + def test_unapplied(self): |
957 | + self.make_empty_quilt_dir("source") |
958 | + self.build_tree_contents([ |
959 | + ("source/patches/series", "patch1.diff\n"), |
960 | + ("source/patches/patch1.diff", "foob ar")]) |
961 | + self.assertEquals(["patch1.diff"], quilt_unapplied("source")) |
962 | + |
963 | + def test_unapplied_multi(self): |
964 | + self.make_empty_quilt_dir("source") |
965 | + self.build_tree_contents([ |
966 | + ("source/patches/series", "patch1.diff\npatch2.diff"), |
967 | + ("source/patches/patch1.diff", "foob ar"), |
968 | + ("source/patches/patch2.diff", "bazb ar")]) |
969 | + self.assertEquals(["patch1.diff", "patch2.diff"], |
970 | + quilt_unapplied("source", "patches")) |
971 | |
972 | === added file 'breezy/plugins/quilt/wrapper.py' |
973 | --- breezy/plugins/quilt/wrapper.py 1970-01-01 00:00:00 +0000 |
974 | +++ breezy/plugins/quilt/wrapper.py 2019-06-15 13:39:59 +0000 |
975 | @@ -0,0 +1,230 @@ |
976 | +# quilt.py -- Quilt patch handling |
977 | +# Copyright (C) 2011 Canonical Ltd. |
978 | +# Copyright (C) 2019 Jelmer Verooij <jelmer@jelmer.uk> |
979 | +# |
980 | +# Breezy is free software; you can redistribute it and/or modify |
981 | +# it under the terms of the GNU General Public License as published by |
982 | +# the Free Software Foundation; either version 2 of the License, or |
983 | +# (at your option) any later version. |
984 | +# |
985 | +# Breezy is distributed in the hope that it will be useful, |
986 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
987 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
988 | +# GNU General Public License for more details. |
989 | +# |
990 | +# You should have received a copy of the GNU General Public License |
991 | +# along with Breezy; if not, write to the Free Software |
992 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
993 | +# |
994 | + |
995 | +"""Quilt patch handling.""" |
996 | + |
997 | +from __future__ import absolute_import |
998 | + |
999 | +import errno |
1000 | +import os |
1001 | +import signal |
1002 | +import subprocess |
1003 | +from ... import ( |
1004 | + errors, |
1005 | + osutils, |
1006 | + trace, |
1007 | + ) |
1008 | + |
1009 | + |
1010 | +class QuiltError(errors.BzrError): |
1011 | + |
1012 | + _fmt = "An error (%(retcode)d) occurred running quilt: %(stderr)s%(extra)s" |
1013 | + |
1014 | + def __init__(self, retcode, stdout, stderr): |
1015 | + self.retcode = retcode |
1016 | + self.stderr = stderr |
1017 | + if stdout is not None: |
1018 | + self.extra = "\n\n%s" % stdout |
1019 | + else: |
1020 | + self.extra = "" |
1021 | + self.stdout = stdout |
1022 | + |
1023 | + |
1024 | +class QuiltNotInstalled(errors.BzrError): |
1025 | + |
1026 | + _fmt = "Quilt is not installed." |
1027 | + |
1028 | + |
1029 | +def run_quilt( |
1030 | + args, working_dir, series_file=None, patches_dir=None, quiet=None): |
1031 | + """Run quilt. |
1032 | + |
1033 | + :param args: Arguments to quilt |
1034 | + :param working_dir: Working dir |
1035 | + :param series_file: Optional path to the series file |
1036 | + :param patches_dir: Optional path to the patches |
1037 | + :param quilt: Whether to be quiet (quilt stderr not to terminal) |
1038 | + :raise QuiltError: When running quilt fails |
1039 | + """ |
1040 | + def subprocess_setup(): |
1041 | + signal.signal(signal.SIGPIPE, signal.SIG_DFL) |
1042 | + env = {} |
1043 | + if patches_dir is not None: |
1044 | + env["QUILT_PATCHES"] = patches_dir |
1045 | + else: |
1046 | + env["QUILT_PATCHES"] = os.path.join(working_dir, "patches") |
1047 | + if series_file is not None: |
1048 | + env["QUILT_SERIES"] = series_file |
1049 | + else: |
1050 | + env["QUILT_SERIES"] = os.path.join(env["QUILT_PATCHES"], "series") |
1051 | + # Hide output if -q is in use. |
1052 | + if quiet is None: |
1053 | + quiet = trace.is_quiet() |
1054 | + if not quiet: |
1055 | + stderr = subprocess.STDOUT |
1056 | + else: |
1057 | + stderr = subprocess.PIPE |
1058 | + command = ["quilt"] + args |
1059 | + trace.mutter("running: %r", command) |
1060 | + if not os.path.isdir(working_dir): |
1061 | + raise AssertionError("%s is not a valid directory" % working_dir) |
1062 | + try: |
1063 | + proc = subprocess.Popen( |
1064 | + command, cwd=working_dir, env=env, |
1065 | + stdin=subprocess.PIPE, preexec_fn=subprocess_setup, |
1066 | + stdout=subprocess.PIPE, stderr=stderr) |
1067 | + except OSError as e: |
1068 | + if e.errno != errno.ENOENT: |
1069 | + raise |
1070 | + raise QuiltNotInstalled() |
1071 | + (stdout, stderr) = proc.communicate() |
1072 | + if proc.returncode not in (0, 2): |
1073 | + if stdout is not None: |
1074 | + stdout = stdout.decode() |
1075 | + if stderr is not None: |
1076 | + stderr = stderr.decode() |
1077 | + raise QuiltError(proc.returncode, stdout, stderr) |
1078 | + if stdout is None: |
1079 | + return "" |
1080 | + return stdout |
1081 | + |
1082 | + |
1083 | +def quilt_pop_all( |
1084 | + working_dir, patches_dir=None, series_file=None, quiet=None, |
1085 | + force=False, refresh=False): |
1086 | + """Pop all patches. |
1087 | + |
1088 | + :param working_dir: Directory to work in |
1089 | + :param patches_dir: Optional patches directory |
1090 | + :param series_file: Optional series file |
1091 | + """ |
1092 | + args = ["pop", "-a"] |
1093 | + if force: |
1094 | + args.append("-f") |
1095 | + if refresh: |
1096 | + args.append("--refresh") |
1097 | + return run_quilt( |
1098 | + args, working_dir=working_dir, |
1099 | + patches_dir=patches_dir, series_file=series_file, quiet=quiet) |
1100 | + |
1101 | + |
1102 | +def quilt_pop(working_dir, patch, patches_dir=None, series_file=None, quiet=None): |
1103 | + """Pop a patch. |
1104 | + |
1105 | + :param working_dir: Directory to work in |
1106 | + :param patch: Patch to apply |
1107 | + :param patches_dir: Optional patches directory |
1108 | + :param series_file: Optional series file |
1109 | + """ |
1110 | + return run_quilt( |
1111 | + ["pop", patch], working_dir=working_dir, |
1112 | + patches_dir=patches_dir, series_file=series_file, quiet=quiet) |
1113 | + |
1114 | + |
1115 | +def quilt_push_all(working_dir, patches_dir=None, series_file=None, quiet=None, |
1116 | + force=False, refresh=False): |
1117 | + """Push all patches. |
1118 | + |
1119 | + :param working_dir: Directory to work in |
1120 | + :param patches_dir: Optional patches directory |
1121 | + :param series_file: Optional series file |
1122 | + """ |
1123 | + args = ["push", "-a"] |
1124 | + if force: |
1125 | + args.append("-f") |
1126 | + if refresh: |
1127 | + args.append("--refresh") |
1128 | + return run_quilt( |
1129 | + args, working_dir=working_dir, |
1130 | + patches_dir=patches_dir, series_file=series_file, quiet=quiet) |
1131 | + |
1132 | + |
1133 | +def quilt_push(working_dir, patch, patches_dir=None, series_file=None, quiet=None): |
1134 | + """Push a patch. |
1135 | + |
1136 | + :param working_dir: Directory to work in |
1137 | + :param patch: Patch to push |
1138 | + :param patches_dir: Optional patches directory |
1139 | + :param series_file: Optional series file |
1140 | + """ |
1141 | + return run_quilt( |
1142 | + ["push", patch], working_dir=working_dir, |
1143 | + patches_dir=patches_dir, series_file=series_file, quiet=quiet) |
1144 | + |
1145 | + |
1146 | +def quilt_upgrade(working_dir): |
1147 | + return run_quilt(["upgrade"], working_dir=working_dir) |
1148 | + |
1149 | + |
1150 | +def quilt_applied(tree): |
1151 | + """Find the list of applied quilt patches. |
1152 | + |
1153 | + """ |
1154 | + try: |
1155 | + return [patch.rstrip(b"\n").decode(osutils._fs_enc) |
1156 | + for patch in tree.get_file_lines(".pc/applied-patches") |
1157 | + if patch.strip() != b""] |
1158 | + except errors.NoSuchFile: |
1159 | + return [] |
1160 | + except (IOError, OSError) as e: |
1161 | + if e.errno == errno.ENOENT: |
1162 | + # File has already been removed |
1163 | + return [] |
1164 | + raise |
1165 | + |
1166 | + |
1167 | +def quilt_unapplied(working_dir, patches_dir=None, series_file=None): |
1168 | + """Find the list of unapplied quilt patches. |
1169 | + |
1170 | + :param working_dir: Directory to work in |
1171 | + :param patches_dir: Optional patches directory |
1172 | + :param series_file: Optional series file |
1173 | + """ |
1174 | + try: |
1175 | + unapplied_patches = run_quilt( |
1176 | + ["unapplied"], |
1177 | + working_dir=working_dir, patches_dir=patches_dir, |
1178 | + series_file=series_file).splitlines() |
1179 | + patch_basenames = [] |
1180 | + for patch in unapplied_patches: |
1181 | + patch = os.path.basename(patch) |
1182 | + patch_basenames.append(patch.decode(osutils._fs_enc)) |
1183 | + return patch_basenames |
1184 | + except QuiltError as e: |
1185 | + if e.retcode == 1: |
1186 | + return [] |
1187 | + raise |
1188 | + |
1189 | + |
1190 | +def quilt_series(tree, series_file): |
1191 | + """Find the list of patches. |
1192 | + |
1193 | + :param tree: Tree to read from |
1194 | + """ |
1195 | + try: |
1196 | + return [patch.rstrip(b"\n").decode(osutils._fs_enc) for patch in |
1197 | + tree.get_file_lines(series_file) |
1198 | + if patch.strip() != b""] |
1199 | + except (IOError, OSError) as e: |
1200 | + if e.errno == errno.ENOENT: |
1201 | + # File has already been removed |
1202 | + return [] |
1203 | + raise |
1204 | + except errors.NoSuchFile: |
1205 | + return [] |
1206 | |
1207 | === modified file 'byov.conf' |
1208 | --- byov.conf 2019-06-02 05:13:10 +0000 |
1209 | +++ byov.conf 2019-06-15 13:39:59 +0000 |
1210 | @@ -30,7 +30,7 @@ |
1211 | # FIXME: Arguably this should be vm.build_deps=brz but it requires either an |
1212 | # available package or at least a debian/ dir ? -- vila 2018-02-23 |
1213 | brz.build_deps = gcc, debhelper, python, python-all-dev, python3-all-dev, python-configobj, python3-configobj, python-docutils, python3-docutils, python-paramiko, python3-paramiko, python-subunit, python3-subunit, python-testtools, python3-testtools, subunit, python-pip, python3-pip, python-setuptools, python3-setuptools, python-flake8, python3-flake8, python-sphinx, python3-sphinx, python-launchpadlib, python3-launchpadlib |
1214 | -subunit.build_deps = python-testscenarios, python3-testscenarios, python-testtools, python3-testtools |
1215 | +subunit.build_deps = python-testscenarios, python3-testscenarios, python-testtools, python3-testtools, quilt |
1216 | vm.packages = {brz.build_deps}, {subunit.build_deps}, bzr, git, python-junitxml |
1217 | [brz-xenial] |
1218 | vm.release = xenial |
1219 | |
1220 | === modified file 'doc/en/release-notes/brz-3.1.txt' |
1221 | --- doc/en/release-notes/brz-3.1.txt 2019-06-14 23:34:47 +0000 |
1222 | +++ doc/en/release-notes/brz-3.1.txt 2019-06-15 13:39:59 +0000 |
1223 | @@ -27,6 +27,9 @@ |
1224 | * The 'patch' command is now bundled with brz. |
1225 | Imported from bzrtools by Aaron Bentley. (Jelmer Vernooij) |
1226 | |
1227 | + * The 'quilt' plugin, extracted from brz-debian, is now |
1228 | + bundled. (Jelmer Vernooij) |
1229 | + |
1230 | Improvements |
1231 | ************ |
1232 |
Inclusion seems reasonable. Just see inline note about mix of license headers referencing Breezy or bzr-builddeb as the project.