aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--patchtree/cli.py1
-rw-r--r--patchtree/config.py3
-rw-r--r--patchtree/context.py24
-rw-r--r--patchtree/diff.py22
-rw-r--r--patchtree/patch.py7
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)