Skip to content

Commit 82c71ef

Browse files
committed
Merge remote-tracking branch 'memsharded/feature/new_remove'
2 parents 936e1a0 + 9d166dc commit 82c71ef

File tree

3 files changed

+80
-20
lines changed

3 files changed

+80
-20
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.pyc

example/example.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import os
2+
import logging
3+
from patch import fromfile, fromstring
4+
5+
class PatchLogHandler(logging.Handler):
6+
def __init__(self):
7+
logging.Handler.__init__(self, logging.DEBUG)
8+
9+
def emit(self, record):
10+
logstr = self.format(record)
11+
print logstr
12+
13+
patchlog = logging.getLogger("patch")
14+
patchlog.handlers = []
15+
patchlog.addHandler(PatchLogHandler())
16+
17+
patch = fromstring("""--- /dev/null
18+
+++ b/newfile
19+
@@ -0,0 +0,3 @@
20+
+New file1
21+
+New file2
22+
+New file3
23+
""")
24+
25+
patch.apply(root=os.getcwd(), strip=0)
26+
27+
28+
with open("newfile", "rb") as f:
29+
newfile = f.read()
30+
assert "New file1\nNew file2\nNew file3\n" == newfile
31+
32+
patch = fromstring("""--- a/newfile
33+
+++ /dev/null
34+
@@ -0,3 +0,0 @@
35+
-New file1
36+
-New file2
37+
-New file3
38+
""")
39+
40+
result = patch.apply(root=os.getcwd(), strip=0)
41+
42+
assert os.path.exists("newfile") is False

patch.py

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"""
33
Patch utility to apply unified diffs
44
5-
Brute-force line-by-line non-recursive parsing
5+
Brute-force line-by-line non-recursive parsing
66
77
Copyright (c) 2008-2016 anatoly techtonik
88
Available under the terms of MIT license
@@ -54,7 +54,7 @@ def tostr(b):
5454

5555
# [ ] figure out how to print non-utf-8 filenames without
5656
# information loss
57-
return b.decode('utf-8')
57+
return b.decode('utf-8')
5858

5959

6060
#------------------------------------------------
@@ -233,7 +233,7 @@ class Patch(object):
233233
If used as an iterable, returns hunks.
234234
"""
235235
def __init__(self):
236-
self.source = None
236+
self.source = None
237237
self.target = None
238238
self.hunks = []
239239
self.hunkends = []
@@ -339,7 +339,7 @@ def lineno(self):
339339

340340
# regexp to match start of hunk, used groups - 1,3,4,6
341341
re_hunk_start = re.compile(b"^@@ -(\d+)(,(\d+))? \+(\d+)(,(\d+))? @@")
342-
342+
343343
self.errors = 0
344344
# temp buffers for header and filenames info
345345
header = []
@@ -375,7 +375,7 @@ def lineno(self):
375375
else:
376376
info("%d unparsed bytes left at the end of stream" % len(b''.join(header)))
377377
self.warnings += 1
378-
# TODO check for \No new line at the end..
378+
# TODO check for \No new line at the end..
379379
# TODO test for unparsed bytes
380380
# otherwise error += 1
381381
# this is actually a loop exit
@@ -408,7 +408,7 @@ def lineno(self):
408408
p.hunkends["lf"] += 1
409409
elif line.endswith(b"\r"):
410410
p.hunkends["cr"] += 1
411-
411+
412412
if line.startswith(b"-"):
413413
hunkactual["linessrc"] += 1
414414
elif line.startswith(b"+"):
@@ -519,7 +519,7 @@ def lineno(self):
519519
headscan = True
520520
else:
521521
if tgtname != None:
522-
# XXX seems to be a dead branch
522+
# XXX seems to be a dead branch
523523
warning("skipping invalid patch - double target at line %d" % (lineno+1))
524524
self.errors += 1
525525
srcname = None
@@ -612,7 +612,7 @@ def lineno(self):
612612
warning("error: no patch data found!")
613613
return False
614614
else: # extra data at the end of file
615-
pass
615+
pass
616616
else:
617617
warning("error: patch stream is incomplete!")
618618
self.errors += 1
@@ -638,7 +638,7 @@ def lineno(self):
638638
# --------
639639

640640
self._normalize_filenames()
641-
641+
642642
return (self.errors == 0)
643643

644644
def _detect_type(self, p):
@@ -681,14 +681,14 @@ def _detect_type(self, p):
681681
return GIT
682682

683683
# HG check
684-
#
684+
#
685685
# - for plain HG format header is like "diff -r b2d9961ff1f5 filename"
686686
# - for Git-style HG patches it is "diff --git a/oldname b/newname"
687687
# - filename starts with a/, b/ or is equal to /dev/null
688688
# - exported changesets also contain the header
689689
# # HG changeset patch
690690
# # User name@example.com
691-
# ...
691+
# ...
692692
# TODO add MQ
693693
# TODO add revision info
694694
if len(p.header) > 0:
@@ -817,7 +817,7 @@ def diffstat(self):
817817
hist = "+"*int(iwidth) + "-"*int(dwidth)
818818
# -- /calculating +- histogram --
819819
output += (format % (tostr(names[i]), str(insert[i] + delete[i]), hist))
820-
820+
821821
output += (" %d files changed, %d insertions(+), %d deletions(-), %+d bytes"
822822
% (len(names), sum(insert), sum(delete), delta))
823823
return output
@@ -854,6 +854,10 @@ def findfiles(self, old, new):
854854
return new, new
855855
return None, None
856856

857+
def _strip_prefix(self, filename):
858+
if filename.startswith(b'a/') or filename.startswith(b'b/'):
859+
return filename[2:]
860+
return filename
857861

858862
def apply(self, strip=0, root=None):
859863
""" Apply parsed patch, optionally stripping leading components
@@ -890,12 +894,25 @@ def apply(self, strip=0, root=None):
890894

891895
filenameo, filenamen = self.findfiles(old, new)
892896

893-
if not filenameo or not filenamen:
894-
warning("source/target file does not exist:\n --- %s\n +++ %s" % (old, new))
895-
errors += 1
896-
continue
897-
if not isfile(filenameo):
898-
warning("not a file - %s" % filenameo)
897+
if not filename:
898+
if "dev/null" in old:
899+
# this is a file creation
900+
filename = self._strip_prefix(new)
901+
# I wish there would be something more clean to get the full contents
902+
new_file = "".join(s[1:] for s in p.hunks[0].text)
903+
with open(filename, "wb") as f:
904+
f.write(new_file)
905+
continue
906+
elif "dev/null" in new:
907+
# this is a file removal
908+
os.remove(self._strip_prefix(old))
909+
continue
910+
else:
911+
warning("source/target file does not exist:\n --- %s\n +++ %s" % (old, new))
912+
errors += 1
913+
continue
914+
if not isfile(filename):
915+
warning("not a file - %s" % filename)
899916
errors += 1
900917
continue
901918

@@ -1077,7 +1094,7 @@ class NoMatch(Exception):
10771094

10781095
def patch_stream(self, instream, hunks):
10791096
""" Generator that yields stream patched with hunks iterable
1080-
1097+
10811098
Converts lineends in hunk lines to the best suitable format
10821099
autodetected from input
10831100
"""
@@ -1130,7 +1147,7 @@ def get_line():
11301147
yield line2write.rstrip(b"\r\n")+newline
11311148
else: # newlines are mixed
11321149
yield line2write
1133-
1150+
11341151
for line in instream:
11351152
yield line
11361153

0 commit comments

Comments
 (0)