Merge lp:~jelmer/brz/import-patch into lp:brz

Proposed by Jelmer Vernooij on 2019-04-06
Status: Merged
Approved by: Jelmer Vernooij on 2019-06-01
Approved revision: 7302
Merge reported by: The Breezy Bot
Merged at revision: not available
Proposed branch: lp:~jelmer/brz/import-patch
Merge into: lp:brz
Diff against target: 247 lines (+154/-9)
6 files modified
breezy/builtins.py (+24/-0)
breezy/patch.py (+81/-8)
breezy/tests/blackbox/__init__.py (+1/-0)
breezy/tests/blackbox/test_patch.py (+36/-0)
breezy/tests/test_patch.py (+9/-1)
doc/en/release-notes/brz-3.1.txt (+3/-0)
To merge this branch: bzr merge lp:~jelmer/brz/import-patch
Reviewer Review Type Date Requested Status
Jelmer Vernooij Approve on 2019-06-01
Review via email: mp+365618@code.launchpad.net

Commit message

Import the 'patch' command from bzrtools.

Description of the change

Import the 'patch' command from bzrtools.

To post a comment you must log in.
Jelmer Vernooij (jelmer) :
review: Approve
Jelmer Vernooij (jelmer) :
review: Approve
lp:~jelmer/brz/import-patch updated on 2019-06-01
7302. By Jelmer Vernooij on 2019-06-01

Merge trunk.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/builtins.py'
2--- breezy/builtins.py 2019-05-29 05:24:46 +0000
3+++ breezy/builtins.py 2019-06-01 01:20:30 +0000
4@@ -7001,6 +7001,30 @@
5 grep.versioned_grep(opts)
6
7
8+class cmd_patch(Command):
9+ """Apply a named patch to the current tree.
10+
11+ """
12+ takes_args = ['filename?']
13+ takes_options = [Option('strip', type=int, short_name='p',
14+ help=("Strip the smallest prefix containing num "
15+ "leading slashes from filenames.")),
16+ Option('silent', help='Suppress chatter.')]
17+
18+ def run(self, filename=None, strip=None, silent=False):
19+ from .patch import patch_tree
20+ wt = WorkingTree.open_containing('.')[0]
21+ if strip is None:
22+ strip = 1
23+ my_file = None
24+ if filename is None:
25+ my_file = getattr(sys.stdin, 'buffer', sys.stdin)
26+ else:
27+ my_file = open(filename, 'rb')
28+ patches = [my_file.read()]
29+ return patch_tree(wt, patches, strip, quiet=is_quiet(), out=self.outf)
30+
31+
32 class cmd_resolve_location(Command):
33 __doc__ = """Expand a location to a full URL.
34
35
36=== modified file 'breezy/patch.py'
37--- breezy/patch.py 2017-05-22 00:56:52 +0000
38+++ breezy/patch.py 2019-06-01 01:20:30 +0000
39@@ -1,4 +1,5 @@
40 # Copyright (C) 2005, 2006 Canonical Ltd
41+# Copyright (C) 2005, 2008 Aaron Bentley, 2006 Michael Ellerman
42 #
43 # This program is free software; you can redistribute it and/or modify
44 # it under the terms of the GNU General Public License as published by
45@@ -16,16 +17,30 @@
46
47 from __future__ import absolute_import
48
49+"""Diff and patch functionality"""
50+
51 import errno
52 import os
53 from subprocess import Popen, PIPE
54+import sys
55
56-from .errors import NoDiff3
57+from .errors import NoDiff3, BzrError
58 from .textfile import check_text_path
59
60-"""Diff and patch functionality"""
61-
62-__docformat__ = "restructuredtext"
63+class PatchFailed(BzrError):
64+
65+ _fmt = """Patch application failed"""
66+
67+
68+class PatchInvokeError(BzrError):
69+
70+ _fmt = """Error invoking patch: %(errstr)s%(stderr)s"""
71+ internal_error = False
72+
73+ def __init__(self, e, stderr=''):
74+ self.exception = e
75+ self.errstr = os.strerror(e.errno)
76+ self.stderr = '\n' + stderr
77
78
79 _do_close_fds = True
80@@ -96,9 +111,67 @@
81 raise
82 if status not in (0, 1):
83 raise Exception(stderr)
84- f = open(out_file, 'wb')
85- try:
86+ with open(out_file, 'wb') as f:
87 f.write(output)
88- finally:
89- f.close()
90 return status
91+
92+
93+def patch_tree(tree, patches, strip=0, reverse=False, dry_run=False,
94+ quiet=False, out=None):
95+ """Apply a patch to a tree.
96+
97+ Args:
98+ tree: A MutableTree object
99+ patches: list of patches as bytes
100+ strip: Strip X segments of paths
101+ reverse: Apply reversal of patch
102+ dry_run: Dry run
103+ """
104+ return run_patch(tree.basedir, patches, strip, reverse, dry_run,
105+ quiet, out=out)
106+
107+
108+def run_patch(directory, patches, strip=0, reverse=False, dry_run=False,
109+ quiet=False, _patch_cmd='patch', target_file=None, out=None):
110+ args = [_patch_cmd, '-d', directory, '-s', '-p%d' % strip, '-f']
111+ if quiet:
112+ args.append('--quiet')
113+
114+ if sys.platform == "win32":
115+ args.append('--binary')
116+
117+ if reverse:
118+ args.append('-R')
119+ if dry_run:
120+ if sys.platform.startswith('freebsd'):
121+ args.append('--check')
122+ else:
123+ args.append('--dry-run')
124+ stderr = PIPE
125+ else:
126+ stderr = None
127+ if target_file is not None:
128+ args.append(target_file)
129+
130+ try:
131+ process = Popen(args, stdin=PIPE, stdout=PIPE, stderr=stderr)
132+ except OSError as e:
133+ raise PatchInvokeError(e)
134+ try:
135+ for patch in patches:
136+ process.stdin.write(bytes(patch))
137+ process.stdin.close()
138+
139+ except IOError as e:
140+ raise PatchInvokeError(e, process.stderr.read())
141+
142+ result = process.wait()
143+ if not dry_run:
144+ if out is not None:
145+ out.write(process.stdout.read())
146+ else:
147+ process.stdout.read()
148+ if result != 0:
149+ raise PatchFailed()
150+
151+ return result
152
153=== modified file 'breezy/tests/blackbox/__init__.py'
154--- breezy/tests/blackbox/__init__.py 2019-02-24 00:57:44 +0000
155+++ breezy/tests/blackbox/__init__.py 2019-06-01 01:20:30 +0000
156@@ -92,6 +92,7 @@
157 'test_non_ascii',
158 'test_outside_wt',
159 'test_pack',
160+ 'test_patch',
161 'test_ping',
162 'test_plugins',
163 'test_pull',
164
165=== added file 'breezy/tests/blackbox/test_patch.py'
166--- breezy/tests/blackbox/test_patch.py 1970-01-01 00:00:00 +0000
167+++ breezy/tests/blackbox/test_patch.py 2019-06-01 01:20:30 +0000
168@@ -0,0 +1,36 @@
169+# Copyright (C) 2019 Breezy Developers
170+#
171+# This program is free software; you can redistribute it and/or modify
172+# it under the terms of the GNU General Public License as published by
173+# the Free Software Foundation; either version 2 of the License, or
174+# (at your option) any later version.
175+#
176+# This program is distributed in the hope that it will be useful,
177+# but WITHOUT ANY WARRANTY; without even the implied warranty of
178+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
179+# GNU General Public License for more details.
180+#
181+# You should have received a copy of the GNU General Public License
182+# along with this program; if not, write to the Free Software
183+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
184+
185+
186+from breezy.tests import TestCaseWithTransport
187+
188+
189+class TestPatch(TestCaseWithTransport):
190+
191+ def test_patch(self):
192+ self.run_bzr('init')
193+ with open('myfile', 'w') as f:
194+ f.write('hello')
195+ self.run_bzr('add')
196+ self.run_bzr('commit -m hello')
197+ with open('myfile', 'w') as f:
198+ f.write('goodbye')
199+ with open('mypatch', 'w') as f:
200+ f.write(self.run_bzr('diff -p1', retcode=1)[0])
201+ self.run_bzr('revert')
202+ self.assertFileEqual('hello', 'myfile')
203+ self.run_bzr('patch -p1 --silent mypatch')
204+ self.assertFileEqual('goodbye', 'myfile')
205
206=== modified file 'breezy/tests/test_patch.py'
207--- breezy/tests/test_patch.py 2018-11-11 04:08:32 +0000
208+++ breezy/tests/test_patch.py 2019-06-01 01:20:30 +0000
209@@ -1,4 +1,5 @@
210 # Copyright (C) 2006 Canonical Ltd
211+# Copyright (C) 2008 Aaron Bentley <aaron@aaronbentley.com>
212 #
213 # This program is free software; you can redistribute it and/or modify
214 # it under the terms of the GNU General Public License as published by
215@@ -15,7 +16,7 @@
216 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
217
218 from breezy.errors import BinaryFile
219-from breezy.patch import diff3
220+from breezy.patch import diff3, PatchInvokeError, run_patch
221 from breezy.tests import TestCaseInTempDir
222
223
224@@ -29,3 +30,10 @@
225 with open('base', 'wb') as f:
226 f.write(b'\x00')
227 self.assertRaises(BinaryFile, diff3, 'unused', 'this', 'other', 'base')
228+
229+
230+class TestPatch(TestCaseInTempDir):
231+
232+ def test_missing_patch(self):
233+ self.assertRaises(PatchInvokeError, run_patch, '.', [],
234+ _patch_cmd='/unlikely/to/exist')
235
236=== modified file 'doc/en/release-notes/brz-3.1.txt'
237--- doc/en/release-notes/brz-3.1.txt 2019-03-06 14:23:50 +0000
238+++ doc/en/release-notes/brz-3.1.txt 2019-06-01 01:20:30 +0000
239@@ -21,6 +21,9 @@
240
241 .. New commands, options, etc that users may wish to try out.
242
243+ * The 'patch' command is now bundled with brz.
244+ Imported from bzrtools by Aaron Bentley. (Jelmer Vernooń≥)
245+
246 Improvements
247 ************
248

Subscribers

People subscribed via source and target branches