diff options
| -rw-r--r-- | patchtree/cli.py | 1 | ||||
| -rw-r--r-- | patchtree/config.py | 3 | ||||
| -rw-r--r-- | patchtree/context.py | 24 | ||||
| -rw-r--r-- | patchtree/diff.py | 22 | ||||
| -rw-r--r-- | patchtree/patch.py | 7 |
5 files changed, 39 insertions, 18 deletions
diff --git a/patchtree/cli.py b/patchtree/cli.py index c5672f0..1c38d30 100644 --- a/patchtree/cli.py +++ b/patchtree/cli.py @@ -73,6 +73,7 @@ def parse_arguments(config: Config) -> Context: metavar="DIR", help="patchset root directory", type=path_dir, + default=config.default_root, ) parser.add_argument( "-g", diff --git a/patchtree/config.py b/patchtree/config.py index 48f11fb..a1a2b0f 100644 --- a/patchtree/config.py +++ b/patchtree/config.py @@ -132,5 +132,8 @@ class Config: default_patch_sources: list[Path] = field(default_factory=list) """List of default sources.""" + default_root: str | None = None + """Default value of the -C argument.""" + def __post_init__(self): self.processors = {**DEFAULT_PROCESSORS, **self.processors} diff --git a/patchtree/context.py b/patchtree/context.py index ef32e97..8635e92 100644 --- a/patchtree/context.py +++ b/patchtree/context.py @@ -34,7 +34,7 @@ class FS: raise NotImplementedError() - def get_content(self, file: str) -> str | None: + def get_content(self, file: str) -> bytes | str | None: """ Get the content of a file relative to the target. @@ -63,11 +63,11 @@ class DiskFS(FS): def __init__(self, target): super(DiskFS, self).__init__(target) - def get_dir(self, dir: str) -> list[str]: + def get_dir(self, dir): here = self.target.joinpath(dir) return [path.name for path in here.iterdir()] - def get_content(self, file: str) -> str | None: + def get_content(self, file): here = self.target.joinpath(file) if not here.exists(): return None @@ -75,9 +75,9 @@ class DiskFS(FS): try: return bytes.decode() except: - return "" + return bytes - def get_mode(self, file: str) -> int: + def get_mode(self, file): here = self.target.joinpath(file) if not here.exists(): return 0 @@ -111,7 +111,7 @@ class ZipFS(FS): return self.files.get(Path(path), None) - def get_dir(self, dir: str) -> list[str]: + def get_dir(self, dir): items: set[str] = set() dir = path.normpath("/" + dir) for zip_dir in self.zip.namelist(): @@ -125,7 +125,7 @@ class ZipFS(FS): items.add(top_level) return list(items) - def get_content(self, file: str) -> str | None: + def get_content(self, file): info = self.get_info(file) if info is None: return None @@ -133,7 +133,7 @@ class ZipFS(FS): try: return bytes.decode() except: - return "" + return bytes def is_implicit_dir(self, file: str) -> bool: """ @@ -151,7 +151,7 @@ class ZipFS(FS): return True return False - def get_mode(self, file: str) -> int: + def get_mode(self, file): MODE_NONEXISTANT = 0 MODE_FILE = 0o644 | S_IFREG MODE_DIR = 0o755 | S_IFDIR @@ -213,9 +213,9 @@ class Context: def collect_inputs(self, options: Namespace) -> list[Path]: inputs: set[Path] = set() - if len(inputs) == 0: + if len(inputs) == 0 and options.root is not None: options.glob = True - options.patch = [str(Path(options.root or ".").joinpath("**"))] + options.patch = [str(Path(options.root).joinpath("**"))] if options.glob: for pattern in options.patch: @@ -237,7 +237,7 @@ class Context: def get_dir(self, dir: str) -> list[str]: return self.fs.get_dir(dir) - def get_content(self, file: str) -> str | None: + def get_content(self, file: str) -> bytes | str | None: return self.fs.get_content(file) def get_mode(self, file: str) -> int: diff --git a/patchtree/diff.py b/patchtree/diff.py index 3dec600..9b68350 100644 --- a/patchtree/diff.py +++ b/patchtree/diff.py @@ -10,10 +10,14 @@ if TYPE_CHECKING: @dataclass class File: - content: str | None + content: str | bytes | None mode: int + def is_binary(self) -> bool: + return isinstance(self.content, bytes) + def lines(self) -> list[str]: + assert not isinstance(self.content, bytes) return (self.content or "").splitlines() @@ -65,9 +69,17 @@ class Diff: delta += f"new mode {b.mode:06o}\n" if a.content != b.content: - lines_a = a.lines() - lines_b = b.lines() - diff = unified_diff(lines_a, lines_b, fromfile, tofile, lineterm="", n=self.config.diff_context) - delta += "".join(f"{line}\n" for line in diff) + # make sure a file doesn't switch from text to binary or vice versa + assert a.is_binary() == b.is_binary() + + if not b.is_binary(): + lines_a = a.lines() + lines_b = b.lines() + diff = unified_diff( + lines_a, lines_b, fromfile, tofile, lineterm="", n=self.config.diff_context + ) + delta += "".join(f"{line}\n" for line in diff) + else: + delta += f"Binary files {fromfile} and {tofile} differ\n" return delta diff --git a/patchtree/patch.py b/patchtree/patch.py index bff7ee4..c393ca1 100644 --- a/patchtree/patch.py +++ b/patchtree/patch.py @@ -53,9 +53,14 @@ class Patch: ) diff.b = File( - content=self.patch.read_text(), + content=None, mode=self.patch.stat().st_mode, ) + b_content = self.patch.read_bytes() + try: + diff.b.content = b_content.decode() + except: + diff.b.content = b_content for cls, args in self.processors: processor = cls(context, args) |