spatch

Lenient universal diff patcher
git clone https://git.sinitax.com/sinitax/spatch
Log | Files | Refs | LICENSE | sfeed.txt

commit 90f9c58e7de29b29fbaf7e78989184ddaaf9a41d
parent 30a754d8c211a76539f1a742d6a5b008c02e8678
Author: Louis Burda <quent.burda@gmail.com>
Date:   Mon, 15 Mar 2021 23:00:38 +0100

replaced install script with general do.sh and implemented handling of no newline at EOF as well as patch prefix

Diffstat:
DTODO | 2--
Ado.sh | 15+++++++++++++++
Dinstall | 3---
Mpatch.py | 48+++++++++++++++++++++++++++++++++++++++++++-----
4 files changed, 58 insertions(+), 10 deletions(-)

diff --git a/TODO b/TODO @@ -1,2 +0,0 @@ -add handling of lines with: -\ No newline at end of file diff --git a/do.sh b/do.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +BINDIR=${BINDIR:-/usr/bin} + +set -e + +if [ "$1" == "install" ]; then + cp patch.py "$DESTDIR/$BINDIR/spatch" + chmod +x "$DESTDIR/$BINDIR/spatch" +elif [ "$1" == "uninstall" ]; then + rm "$DESTDIR/$BINDIR/spatch" +else + echo "USAGE: do.sh (install|uninstall)" + exit 1 +fi diff --git a/install b/install @@ -1,3 +0,0 @@ -#!/bin/bash - -sudo ln -sf $PWD/patch.py /usr/bin/spatch diff --git a/patch.py b/patch.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import sys, os, re +from os import path file_header = """\ --- ([^\n ]*)( [^\n]*)? @@ -21,8 +22,9 @@ def patch_file(src_filename, dst_filename, content): chunks = list() next_header_match = True # for do_while loop - while (next_header_match): - next_header_match = chunk_header_pattern.search(content, prev_header_match.span()[1]) + while next_header_match: + next_header_match = chunk_header_pattern\ + .search(content, prev_header_match.span()[1]) chunks.append(prev_header_match) prev_header_match = next_header_match @@ -47,17 +49,33 @@ def patch_file(src_filename, dst_filename, content): chunk_content = content[start_pos:end_pos].split("\n") valid_lines = 0 for l in chunk_content: - if len(l) == 0 or l[0] not in (' ', '+', '-'): + if len(l) == 0 or l[0] not in (' ', '+', '-', '\\'): break valid_lines += 1 + chunk_content = chunk_content[:valid_lines] src_lines = "\n".join([l[1:] for l in chunk_content if l[0] in (' ', '-')]) dst_lines = "\n".join([l[1:] for l in chunk_content if l[0] in (' ', '+')]) + if src_lines == 0 and dst_lines == 0: + print("[ERROR] Chunk has no valid lines") + sys.exit(1) + + src_nl = dst_nl = True + for i,l in enumerate(chunk_content): + if i != 0 and l == '\ No newline at end of file': + if chunk_content[i-1][0] == '+': + src_nl = False + elif chunk_content[i-1][0] == '-': + dst_nl = False + src_lines += "\n" if src_nl else "" + dst_lines += "\n" if dst_nl else "" + try: replace_start = src_content.index(src_lines) - src_content = src_content[:replace_start] + dst_lines + src_content[replace_start+len(src_lines):] + src_content = src_content[:replace_start] \ + + dst_lines + src_content[replace_start+len(src_lines):] except Exception as e: print("[ERROR] Failed to find corresponding lines for chunk, exiting..") sys.exit(1) @@ -68,13 +86,18 @@ def main(): if len(sys.argv) < 2: print("Supply the path of a unified diff file as argument") return 1 + elif len(sys.argv) == 3: + targetdir = sys.argv[2] + else: + targetdir = None + noprompt = (os.getenv("NOPROMPT") != None) diff_file = sys.argv[1] content = open(diff_file).read() prev_header_match = file_header_pattern.search(content) if prev_header_match == None: - print("Not a unified diff file!") + print("[ERROR] Not a unified diff file!") return 1 header_matches = list() next_header_match = True # for do_while loop @@ -94,6 +117,21 @@ def main(): src_file = header_matches[i].group(1) dst_file = header_matches[i].group(2) + if src_file.split("/",1)[0] == "a" and dst_file.split("/",1)[0] == "b" \ + and not path.isdir("a") and not path.isdir("b"): + if path.isfile(src_file.split("/",1)[1]): + src_file = src_file.split("/",1)[1] + dst_file = dst_file.split("/",1)[1] + else: + if not targetdir: + if noprompt: + print("[ERROR] Unknown patch prefix") + sys.exit(1) + else: + targetdir = input("patch prefix: ") + src_file = targetdir + "/" + src_file.split("/",1)[1] + dst_file = targetdir + "/" + dst_file.split("/",1)[1] + print("[PATCH] Applying patch from {} to {}".format(src_file, dst_file)) startpos = header_matches[i].span()[1]