Merge lp:~jelmer/brz/patch-api 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/patch-api
Merge into: lp:brz
Diff against target: 110 lines (+65/-4)
3 files modified
breezy/bzr/bundle/bundle_data.py (+0/-2)
breezy/patch.py (+25/-0)
breezy/tests/test_patch.py (+40/-2)
To merge this branch: bzr merge lp:~jelmer/brz/patch-api
Reviewer Review Type Date Requested Status
Jelmer Vernooij Approve
Review via email: mp+375876@code.launchpad.net

Commit message

Add iter_patched_from_hunks implementation that calls out to the patch command.

Description of the change

Add iter_patched_from_hunks implementation that calls out to the patch command.

To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'breezy/bzr/bundle/bundle_data.py'
2--- breezy/bzr/bundle/bundle_data.py 2019-09-27 02:16:49 +0000
3+++ breezy/bzr/bundle/bundle_data.py 2019-11-22 05:22:05 +0000
4@@ -624,8 +624,6 @@
5 except NoSuchId:
6 patch_original = None
7 else:
8- if old_path is None:
9- import pdb; pdb.set_trace()
10 patch_original = self.base_tree.get_file(old_path)
11 file_patch = self.patches.get(path)
12 if file_patch is None:
13
14=== modified file 'breezy/patch.py'
15--- breezy/patch.py 2019-11-19 18:10:28 +0000
16+++ breezy/patch.py 2019-11-22 05:22:05 +0000
17@@ -23,6 +23,7 @@
18 import os
19 from subprocess import Popen, PIPE
20 import sys
21+import tempfile
22
23 from .errors import NoDiff3, BzrError
24 from .textfile import check_text_path
25@@ -175,3 +176,27 @@
26 raise PatchFailed()
27
28 return result
29+
30+
31+def iter_patched_from_hunks(orig_lines, hunks):
32+ """Iterate through a series of lines with a patch applied.
33+ This handles a single file, and does exact, not fuzzy patching.
34+
35+ :param orig_lines: The unpatched lines.
36+ :param hunks: An iterable of Hunk instances.
37+
38+ This is different from breezy.patches in that it invokes the patch
39+ command.
40+ """
41+ with tempfile.NamedTemporaryFile() as f:
42+ f.writelines(orig_lines)
43+ f.flush()
44+ # TODO(jelmer): Stream patch contents to command, rather than
45+ # serializing the entire patch upfront.
46+ serialized = b''.join([hunk.as_bytes() for hunk in hunks])
47+ args = ["patch", "-f", "-s", "--posix", "--binary",
48+ "-o", "-", f.name, "-r", "-"]
49+ stdout, stderr, status = write_to_cmd(args, serialized)
50+ if status == 0:
51+ return [stdout]
52+ raise PatchFailed(stderr)
53
54=== modified file 'breezy/tests/test_patch.py'
55--- breezy/tests/test_patch.py 2019-04-06 12:28:50 +0000
56+++ breezy/tests/test_patch.py 2019-11-22 05:22:05 +0000
57@@ -15,9 +15,13 @@
58 # along with this program; if not, write to the Free Software
59 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
60
61+import os
62+
63+from breezy.iterablefile import IterableFile
64 from breezy.errors import BinaryFile
65-from breezy.patch import diff3, PatchInvokeError, run_patch
66-from breezy.tests import TestCaseInTempDir
67+from breezy.patch import diff3, PatchInvokeError, run_patch, iter_patched_from_hunks
68+from breezy.patches import parse_patch
69+from breezy.tests import TestCaseInTempDir, TestCase
70
71
72 class TestPatch(TestCaseInTempDir):
73@@ -37,3 +41,37 @@
74 def test_missing_patch(self):
75 self.assertRaises(PatchInvokeError, run_patch, '.', [],
76 _patch_cmd='/unlikely/to/exist')
77+
78+
79+class PatchesTester(TestCase):
80+
81+ def datafile(self, filename):
82+ data_path = os.path.join(os.path.dirname(__file__),
83+ "test_patches_data", filename)
84+ return open(data_path, "rb")
85+
86+ def data_lines(self, filename):
87+ with self.datafile(filename) as datafile:
88+ return datafile.readlines()
89+
90+ def test_iter_patched_from_hunks(self):
91+ """Test a few patch files, and make sure they work."""
92+ files = [
93+ ('diff-2', 'orig-2', 'mod-2'),
94+ ('diff-3', 'orig-3', 'mod-3'),
95+ ('diff-4', 'orig-4', 'mod-4'),
96+ ('diff-5', 'orig-5', 'mod-5'),
97+ ('diff-6', 'orig-6', 'mod-6'),
98+ ('diff-7', 'orig-7', 'mod-7'),
99+ ]
100+ for diff, orig, mod in files:
101+ parsed = parse_patch(self.datafile(diff))
102+ orig_lines = list(self.datafile(orig))
103+ mod_lines = list(self.datafile(mod))
104+ iter_patched = iter_patched_from_hunks(orig_lines, parsed.hunks)
105+ patched_file = IterableFile(iter_patched)
106+ count = 0
107+ for patch_line in patched_file:
108+ self.assertEqual(patch_line, mod_lines[count], 'for file %s' % diff)
109+ count += 1
110+ self.assertEqual(count, len(mod_lines))

Subscribers

People subscribed via source and target branches