Merge lp:~jelmer/brz/bundle-quilt into lp:brz

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
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
Martin Packman (gz) wrote :

Inclusion seems reasonable. Just see inline note about mix of license headers referencing Breezy or bzr-builddeb as the project.

review: Approve
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote :
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote :

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

Subscribers

People subscribed via source and target branches