Skip to content

Commit 24f1714

Browse files
author
Thomas Grainger
committed
support for reverse patching, Fixes #10
1 parent 4f31dc8 commit 24f1714

File tree

2 files changed

+53
-43
lines changed

2 files changed

+53
-43
lines changed

tests/test_apply.py

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@
77
import unittest
88

99

10+
def _apply(src, diff_text, reverse=False, use_patch=False):
11+
diff = next(wtp.parse_patch(diff_text))
12+
return wtp.apply.apply_diff(diff, src, reverse, use_patch)
13+
14+
15+
def _apply_r(src, diff_text, reverse=True, use_patch=False):
16+
return _apply(src, diff_text, reverse, use_patch)
17+
18+
1019
class ApplyTestSuite(unittest.TestCase):
1120
"""Basic test cases."""
1221

@@ -27,38 +36,29 @@ def test_diff_default(self):
2736
with open('tests/casefiles/diff-default.diff') as f:
2837
diff_text = f.read()
2938

30-
diff = next(wtp.parse_patch(diff_text))
31-
32-
new_text = wtp.apply.apply_diff(diff, self.lao)
33-
self.assertEqual(new_text, self.tzu)
39+
self.assertEqual(_apply(self.lao, diff_text), self.tzu)
40+
self.assertEqual(_apply_r(self.tzu, diff_text), self.lao)
3441

3542
def test_diff_context(self):
3643
with open('tests/casefiles/diff-context.diff') as f:
3744
diff_text = f.read()
3845

39-
diff = next(wtp.parse_patch(diff_text))
40-
41-
new_text = wtp.apply.apply_diff(diff, self.lao)
42-
self.assertEqual(new_text, self.tzu)
46+
self.assertEqual(_apply(self.lao, diff_text), self.tzu)
47+
self.assertEqual(_apply_r(self.tzu, diff_text), self.lao)
4348

4449
def test_diff_unified(self):
4550
with open('tests/casefiles/diff-unified.diff') as f:
4651
diff_text = f.read()
4752

48-
diff = next(wtp.parse_patch(diff_text))
49-
50-
new_text = wtp.apply.apply_diff(diff, self.lao)
51-
52-
self.assertEqual(new_text, self.tzu)
53+
self.assertEqual(_apply(self.lao, diff_text), self.tzu)
54+
self.assertEqual(_apply_r(self.tzu, diff_text), self.lao)
5355

5456
def test_diff_unified_bad(self):
5557
with open('tests/casefiles/diff-unified-bad.diff') as f:
5658
diff_text = f.read()
5759

58-
diff = next(wtp.parse_patch(diff_text))
59-
6060
with assert_raises(exceptions.ApplyException) as ec:
61-
wtp.apply.apply_diff(diff, self.lao)
61+
_apply(self.lao, diff_text)
6262

6363
e = ec.exception
6464
e_str = str(e)
@@ -71,10 +71,8 @@ def test_diff_unified_bad2(self):
7171
with open('tests/casefiles/diff-unified-bad2.diff') as f:
7272
diff_text = f.read()
7373

74-
diff = next(wtp.parse_patch(diff_text))
75-
7674
with assert_raises(exceptions.ApplyException) as ec:
77-
wtp.apply.apply_diff(diff, self.lao)
75+
_apply(self.lao, diff_text)
7876

7977
e = ec.exception
8078
e_str = str(e)
@@ -87,10 +85,8 @@ def test_diff_unified_bad_backward(self):
8785
with open('tests/casefiles/diff-unified-bad2.diff') as f:
8886
diff_text = f.read()
8987

90-
diff = next(wtp.parse_patch(diff_text))
91-
9288
with assert_raises(exceptions.ApplyException) as ec:
93-
wtp.apply.apply_diff(diff, self.tzu)
89+
_apply(self.tzu, diff_text)
9490

9591
e = ec.exception
9692
e_str = str(e)
@@ -103,10 +99,8 @@ def test_diff_unified_bad_empty_source(self):
10399
with open('tests/casefiles/diff-unified-bad2.diff') as f:
104100
diff_text = f.read()
105101

106-
diff = next(wtp.parse_patch(diff_text))
107-
108102
with assert_raises(exceptions.ApplyException) as ec:
109-
wtp.apply.apply_diff(diff, '')
103+
_apply('', diff_text)
110104

111105
e = ec.exception
112106
e_str = str(e)
@@ -119,39 +113,42 @@ def test_diff_unified_patchutil(self):
119113
with open('tests/casefiles/diff-unified.diff') as f:
120114
diff_text = f.read()
121115

122-
diff = next(wtp.parse_patch(diff_text))
116+
self.assertEqual(
117+
_apply(self.lao, diff_text, use_patch=True),
118+
(self.tzu, None),
119+
)
120+
self.assertEqual(
121+
_apply_r(self.tzu, diff_text, use_patch=True),
122+
(self.lao, None),
123+
)
123124

124-
new_text = wtp.apply.apply_diff(diff, self.lao, use_patch=True)
125+
new_text = _apply(self.lao, diff_text, use_patch=True)
125126
self.assertEqual(new_text, (self.tzu, None))
126127

128+
def _do_apply():
129+
return _apply([''] + self.lao, diff_text, use_patch=True)
130+
127131
self.assertRaises(
128132
exceptions.ApplyException,
129-
wtp.apply.apply_diff,
130-
diff,
131-
[''] + self.lao,
132-
use_patch=True,
133+
_do_apply,
133134
)
134135

135136
def test_diff_rcs(self):
136137
with open('tests/casefiles/diff-rcs.diff') as f:
137138
diff_text = f.read()
138139

139-
diff = next(wtp.parse_patch(diff_text))
140+
new_text = _apply(self.lao, diff_text)
140141

141-
new_text = wtp.apply.apply_diff(diff, self.lao)
142142
self.assertEqual(new_text, self.tzu)
143143

144144
def test_diff_ed(self):
145-
self.maxDiff = None
146145
with open('tests/casefiles/diff-ed.diff') as f:
147146
diff_text = f.read()
148147

149-
diff = next(wtp.parse_patch(diff_text))
150-
151-
new_text = wtp.apply.apply_diff(diff, self.lao)
148+
new_text = _apply(self.lao, diff_text)
152149
self.assertEqual(self.tzu, new_text)
153150

154-
new_text = wtp.apply.apply_diff(diff, self.lao, use_patch=True)
151+
new_text = _apply(self.lao, diff_text, use_patch=True)
155152
self.assertEqual(new_text, (self.tzu, None))
156153

157154

whatthepatch/apply.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def apply_patch(diffs):
2525
f.write(new_text)
2626

2727

28-
def _apply_diff_with_subprocess(diff, lines):
28+
def _apply_diff_with_subprocess(diff, lines, reverse=False):
2929
# call out to patch program
3030
patchexec = which('patch')
3131
if not patchexec:
@@ -43,6 +43,7 @@ def _apply_diff_with_subprocess(diff, lines):
4343
f.write(diff.text)
4444

4545
args = [patchexec,
46+
'--reverse' if reverse else '--forward',
4647
'--quiet',
4748
'-o', newfilepath,
4849
'-i', patchfilepath,
@@ -72,18 +73,30 @@ def _apply_diff_with_subprocess(diff, lines):
7273
return lines, rejlines
7374

7475

75-
def apply_diff(diff, text, use_patch=False):
76+
def _reverse(changes):
77+
def _reverse_change(c):
78+
return c._replace(
79+
old=c.new,
80+
new=c.old,
81+
)
82+
83+
return [_reverse_change(c) for c in changes]
84+
85+
86+
def apply_diff(diff, text, reverse=False, use_patch=False):
7687
try:
7788
lines = text.splitlines()
7889
except AttributeError:
7990
lines = list(text)
8091

8192
if use_patch:
82-
return _apply_diff_with_subprocess(diff, lines)
93+
return _apply_diff_with_subprocess(diff, lines, reverse)
8394

8495
n_lines = len(lines)
96+
97+
changes = _reverse(diff.changes) if reverse else diff.changes
8598
# check that the source text matches the context of the diff
86-
for old, new, hunk, line in diff.changes:
99+
for old, new, hunk, line in changes:
87100
# might have to check for line is None here for ed scripts
88101
if old is not None and line is not None:
89102
if old > n_lines:
@@ -108,7 +121,7 @@ def apply_diff(diff, text, use_patch=False):
108121
r = 0
109122
i = 0
110123

111-
for old, new, hunk, line in diff.changes:
124+
for old, new, hunk, line in changes:
112125
if old is not None and new is None:
113126
del lines[old-1-r+i]
114127
r += 1

0 commit comments

Comments
 (0)