From 7b703051076bdec99592a31cd78a83ac79e34fdb Mon Sep 17 00:00:00 2001 From: Loek Le Blansch Date: Fri, 24 Oct 2025 21:46:31 +0200 Subject: add --in-place option --- .editorconfig | 10 ++++++ patchtree/cli.py | 9 ++++-- patchtree/context.py | 89 ++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 81 insertions(+), 27 deletions(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a5ddde0 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = false +max_line_length = 80 + +[*.py] +indent_size = 4 +indent_style = space diff --git a/patchtree/cli.py b/patchtree/cli.py index 5ec8213..7b1a108 100644 --- a/patchtree/cli.py +++ b/patchtree/cli.py @@ -27,6 +27,12 @@ def parse_arguments(config: Config) -> Context: help="output file (stdout by default)", type=str, ) + parser.add_argument( + "-i", + "--in-place", + help="patch target in-place", + action="store_true", + ) parser.add_argument("target", help="target directory or archive") parser.add_argument("patch", help="patch input glob(s)", nargs="+") @@ -61,9 +67,6 @@ def main(): patch = config.patch(config, file) patch.write(context) - context.output.flush() - context.output.close() - return 0 diff --git a/patchtree/context.py b/patchtree/context.py index 8fa7abd..a91fb36 100644 --- a/patchtree/context.py +++ b/patchtree/context.py @@ -1,44 +1,55 @@ from argparse import Namespace -from typing import Union, IO +from typing import Union, IO, cast from pathlib import Path as DiskPath from zipfile import is_zipfile, Path as ZipPath +from tempfile import TemporaryFile from os import path -from sys import stdout +from sys import stdout, stderr +from subprocess import run -class Context: - """ - Generic "context" struct that Diff and the main() function depend on for - application context. - """ +Path = Union[DiskPath, ZipPath] + - fs: Union[DiskPath, ZipPath] - output: IO = stdout +class Context: + fs: Path + output: IO options: Namespace + cache: DiskPath + def __init__(self, options: Namespace): self.options = options + self.fs = self.get_fs() + self.output = self.get_output() - target = options.target - if not path.exists(target): - raise Exception(f"cannot open `{target}'") + if self.options.in_place: + location = cast(DiskPath, self.fs) + self.cache = location.joinpath(".patchtree.diff") + if self.cache.exists(): + run( + ("git", "apply", "--reverse", str(self.cache.absolute())), + cwd=str(location.absolute()), + ) - if options.out is not None: - if options.out == "-": - self.output = stdout - else: - self.output = open(options.out, "w+") + def __del__(self): + self.output.flush() - if path.isdir(target): - self.fs = DiskPath(target) - elif is_zipfile(target): - self.fs = ZipPath(target) - else: - raise Exception("cannot read `{target}'") + if self.options.in_place: + self.output.seek(0) + patch = self.output.read() + location = cast(DiskPath, self.fs) + if len(patch) > 0: + self.cache.write_text(patch) + run( + ("git", "apply", str(self.cache.absolute())), + cwd=str(location.absolute()), + ) + + self.output.close() def get_dir(self, dir: str) -> list[str]: here = self.fs.joinpath(dir) - print(here) return [path.name for path in here.iterdir()] def get_content(self, file: str) -> str | None: @@ -46,3 +57,33 @@ class Context: if not here.exists(): return None return here.read_bytes().decode() + + def get_fs(self) -> Path: + target: str = self.options.target + + if not path.exists(target): + raise Exception(f"cannot open `{target}'") + + if path.isdir(target): + return DiskPath(target) + + if is_zipfile(target): + if self.options.in_place: + raise Exception("cannot edit zip in-place!") + return ZipPath(target) + + raise Exception("cannot read `{target}'") + + def get_output(self) -> IO: + if self.options.in_place: + if self.options.out is not None: + print("warning: --out is ignored when using --in-place", file=stderr) + return TemporaryFile("w+") + + if self.options.out is not None: + if self.options.out == "-": + return stdout + else: + return open(self.options.out, "w+") + + return stdout -- cgit v1.2.3